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:
parent
9b6fa4593a
commit
304c7635da
11 changed files with 815 additions and 125 deletions
289
src/indexer/album.c
Normal file
289
src/indexer/album.c
Normal 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
|
||||||
|
);
|
||||||
|
}
|
|
@ -17,17 +17,22 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <glib-2.0/glib-object.h>
|
#include <glib-2.0/glib-object.h>
|
||||||
|
#include "file.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
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);
|
G_DECLARE_FINAL_TYPE (KotoIndexedAlbum, koto_indexed_album, KOTO, INDEXED_ALBUM, GObject);
|
||||||
|
|
||||||
KotoIndexedAlbum* koto_indexed_album_new(const gchar *path);
|
KotoIndexedAlbum* koto_indexed_album_new(const gchar *path);
|
||||||
|
|
||||||
void add_song(KotoIndexedAlbum *self, KotoIndexedFile *file);
|
void koto_indexed_album_add_file(KotoIndexedAlbum *self, KotoIndexedFile *file);
|
||||||
KotoIndexedFile** get_songs(KotoIndexedAlbum *self);
|
gchar* koto_indexed_album_get_album_art(KotoIndexedAlbum *self);
|
||||||
void remove_song(KotoIndexedAlbum *self, KotoIndexedFile *file);
|
GList* koto_indexed_album_get_files(KotoIndexedAlbum *self);
|
||||||
void remove_song_by_name(KotoIndexedAlbum *self, gchar *file_name);
|
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
|
G_END_DECLS
|
||||||
|
|
196
src/indexer/artist.c
Normal file
196
src/indexer/artist.c
Normal 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
|
||||||
|
);
|
||||||
|
}
|
|
@ -17,17 +17,22 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <glib-2.0/glib-object.h>
|
#include <glib-2.0/glib-object.h>
|
||||||
|
#include "album.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
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);
|
G_DECLARE_FINAL_TYPE (KotoIndexedArtist, koto_indexed_artist, KOTO, INDEXED_ARTIST, GObject);
|
||||||
|
|
||||||
KotoIndexedArtist* koto_indexed_artist_new(const gchar *path);
|
KotoIndexedArtist* koto_indexed_artist_new(const gchar *path);
|
||||||
|
|
||||||
void add_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
|
void koto_indexed_artist_add_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
|
||||||
KotoIndexedAlbum** get_albums(KotoIndexedArtist *self);
|
guint koto_indexed_artist_find_album_with_name(gconstpointer *album_data, gconstpointer *album_name_data);
|
||||||
void remove_album(KotoIndexedArtist *self, KotoIndexedAlbum *album);
|
GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self);
|
||||||
void remove_album_by_name(KotoIndexedArtist *self, gchar *album_name);
|
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
|
G_END_DECLS
|
||||||
|
|
|
@ -17,27 +17,14 @@
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <taglib/tag_c.h>
|
||||||
|
#include "album.h"
|
||||||
|
#include "artist.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "file-indexer.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 {
|
struct _KotoIndexedLibrary {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
gchar *path;
|
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);
|
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) {
|
static void koto_indexed_library_init(KotoIndexedLibrary *self) {
|
||||||
self->music_artists = g_hash_table_new(g_str_hash, g_str_equal);
|
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) {
|
static void koto_indexed_library_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
|
||||||
KotoIndexedLibrary *self = KOTO_INDEXED_LIBRARY(obj);
|
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) {
|
switch (prop_id) {
|
||||||
case PROP_PATH:
|
case PROP_PATH:
|
||||||
self->path = g_strdup(g_value_get_string(val));
|
self->path = g_strdup(g_value_get_string(val));
|
||||||
g_message("Set to %s", self->path);
|
|
||||||
start_indexing(self);
|
start_indexing(self);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -131,11 +165,14 @@ void start_indexing(KotoIndexedLibrary *self) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
index_folder(self, self->path);
|
index_folder(self, self->path, 0);
|
||||||
magic_close(self->magic_cookie);
|
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
|
DIR *dir = opendir(path); // Attempt to open our directory
|
||||||
|
|
||||||
if (dir == NULL) {
|
if (dir == NULL) {
|
||||||
|
@ -145,54 +182,88 @@ void index_folder(KotoIndexedLibrary *self, gchar *path) {
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
|
|
||||||
while ((entry = readdir(dir))) {
|
while ((entry = readdir(dir))) {
|
||||||
if (!g_str_has_prefix(entry->d_name, ".")) { // Not a reference to parent dir, self, or a hidden item
|
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);
|
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 (entry->d_type == DT_DIR) { // Directory
|
||||||
index_folder(self, full_path); // Index this directory
|
if (depth == 1) { // If we are following FOLDER/ARTIST/ALBUM then this would be artist
|
||||||
} else if (entry->d_type == DT_REG) { // Regular file
|
KotoIndexedArtist *artist = koto_indexed_artist_new(full_path); // Attempt to get the artist
|
||||||
index_file(self, full_path); // Index the file
|
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);
|
g_free(full_path);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
closedir(dir); // Close the directory
|
closedir(dir); // Close the directory
|
||||||
}
|
}
|
||||||
|
|
||||||
void index_file(KotoIndexedLibrary *self, gchar *path) {
|
void output_artists(gpointer artist_key, gpointer artist_ptr, gpointer data) {
|
||||||
const char *mime_type = magic_file(self->magic_cookie, path);
|
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
|
g_debug("Artist: %s", artist_name);
|
||||||
return;
|
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 (
|
GList *files = koto_indexed_album_get_files(album); // Get the files for the album
|
||||||
g_str_has_prefix(mime_info[0], "audio/") || // Is audio
|
GList *f;
|
||||||
g_str_has_prefix(mime_info[0], "image/") // Is image
|
|
||||||
) {
|
|
||||||
//g_message("File Name: %s", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_str_has_prefix(mime_info[0], "audio/")) { // Is an audio file
|
for (f = files; f != NULL; f = f->next) {
|
||||||
KotoIndexedFile *file = koto_indexed_file_new(path);
|
KotoIndexedFile *file = (KotoIndexedFile*) f->data;
|
||||||
gchar *filepath;
|
gchar *filepath;
|
||||||
gchar *parsed_name;
|
gchar *parsed_name;
|
||||||
|
guint *pos;
|
||||||
|
|
||||||
g_object_get(file,
|
g_object_get(file,
|
||||||
"path", &filepath,
|
"path", &filepath,
|
||||||
"parsed-name", &parsed_name,
|
"parsed-name", &parsed_name,
|
||||||
|
"position", &pos,
|
||||||
NULL);
|
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(filepath);
|
||||||
g_free(parsed_name);
|
g_free(parsed_name);
|
||||||
g_object_unref(file);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
g_strfreev(mime_info); // Free our mimeinfo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path) {
|
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <glib-2.0/glib-object.h>
|
#include <glib-2.0/glib-object.h>
|
||||||
|
#include "artist.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
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);
|
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 start_indexing(KotoIndexedLibrary *self);
|
||||||
void index_folder(KotoIndexedLibrary *self, gchar *path);
|
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth);
|
||||||
void index_file(KotoIndexedLibrary *self, gchar *path);
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
|
@ -16,17 +16,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
|
#include <taglib/tag_c.h>
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
#include "koto-utils.h"
|
||||||
|
|
||||||
struct _KotoIndexedFile {
|
struct _KotoIndexedFile {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
gchar *path;
|
gchar *path;
|
||||||
|
|
||||||
gchar *file_name;
|
gchar *file_name;
|
||||||
gchar *parsed_name;
|
gchar *parsed_name;
|
||||||
gchar *artist;
|
gchar *artist;
|
||||||
gchar *album;
|
gchar *album;
|
||||||
|
guint *position;
|
||||||
guint position;
|
|
||||||
gboolean acquired_metadata_from_id3;
|
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);
|
g_value_set_string(val, self->parsed_name);
|
||||||
break;
|
break;
|
||||||
case PROP_POSITION:
|
case PROP_POSITION:
|
||||||
g_value_set_uint(val, self->position);
|
g_value_set_uint(val, GPOINTER_TO_UINT(self->position));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
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) {
|
switch (prop_id) {
|
||||||
case PROP_PATH:
|
case PROP_PATH:
|
||||||
self->path = g_strdup(g_value_get_string(val)); // Update our path
|
koto_indexed_file_update_path(self, g_value_get_string(val)); // Update the path
|
||||||
koto_indexed_file_set_file_name(self, g_path_get_basename(self->path)); // Update our file name
|
|
||||||
break;
|
break;
|
||||||
case PROP_ARTIST:
|
case PROP_ARTIST:
|
||||||
self->artist = g_strdup(g_value_get_string(val));
|
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)));
|
koto_indexed_file_set_parsed_name(self, g_strdup(g_value_get_string(val)));
|
||||||
break;
|
break;
|
||||||
case PROP_POSITION:
|
case PROP_POSITION:
|
||||||
self->position = g_value_get_uint(val);
|
koto_indexed_file_set_position(self, g_value_get_uint(val));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
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) {
|
void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name) {
|
||||||
if (new_file_name == NULL) {
|
if (new_file_name == NULL) {
|
||||||
return;
|
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]);
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PARSED_NAME]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void koto_indexed_file_parse_name(KotoIndexedFile *self) {
|
void koto_indexed_file_set_position(KotoIndexedFile *self, guint pos) {
|
||||||
gchar *copied_file_name = g_strdelimit(g_strdup(self->file_name), "_", ' '); // Replace _ with whitespace for starters
|
if (pos == 0) { // No position change really
|
||||||
|
return;
|
||||||
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
|
self->position = GUINT_TO_POINTER(pos);
|
||||||
gchar **split = g_strsplit(copied_file_name, self->album, -1); // Split whenever we encounter the album
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_POSITION]);
|
||||||
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
|
void koto_indexed_file_update_metadata(KotoIndexedFile *self) {
|
||||||
gchar **split = g_strsplit(copied_file_name, " - ", -1); // Split whenever we encounter " - "
|
TagLib_File *t_file = taglib_file_new(self->path); // Get a taglib file for this file
|
||||||
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
|
if ((t_file != NULL) && taglib_file_is_valid(t_file)) { // If we got the taglib file and it is valid
|
||||||
split = g_strsplit(copied_file_name, "-", -1); // Split whenever we encounter -
|
self->acquired_metadata_from_id3 = TRUE;
|
||||||
copied_file_name = g_strjoin("", split, NULL); // Remove entirely
|
TagLib_Tag *tag = taglib_file_tag(t_file); // Get our tag
|
||||||
g_strfreev(split);
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
gchar **split = g_strsplit(copied_file_name, ".", -1); // Split every time we see .
|
taglib_tag_free_strings(); // Free strings
|
||||||
g_free(copied_file_name);
|
taglib_file_free(t_file); // Free the file
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copied_file_name = g_strdup(new_parsed_name);
|
void koto_indexed_file_update_path(KotoIndexedFile *self, const gchar *new_path) {
|
||||||
g_free(new_parsed_name);
|
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);
|
self->path = g_strdup(new_path); // Duplicate the path and set it
|
||||||
g_free(copied_file_name);
|
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) {
|
KotoIndexedFile* koto_indexed_file_new(const gchar *path) {
|
||||||
|
|
|
@ -25,8 +25,11 @@ G_DECLARE_FINAL_TYPE(KotoIndexedFile, koto_indexed_file, KOTO, INDEXED_FILE, GOb
|
||||||
|
|
||||||
KotoIndexedFile* koto_indexed_file_new(const gchar *path);
|
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_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_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
|
G_END_DECLS
|
||||||
|
|
48
src/koto-utils.c
Normal file
48
src/koto-utils.c
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/* koto-utils.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>
|
||||||
|
|
||||||
|
gchar* koto_utils_get_filename_without_extension(gchar *filename) {
|
||||||
|
gchar *trimmed_file_name = g_strdup(filename);
|
||||||
|
gchar **split = g_strsplit(filename, ".", -1); // Split every time we see .
|
||||||
|
g_free(trimmed_file_name);
|
||||||
|
guint len_of_extension_split = g_strv_length(split);
|
||||||
|
|
||||||
|
if (len_of_extension_split == 2) { // Only have two elements
|
||||||
|
trimmed_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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trimmed_file_name = g_strdup(new_parsed_name);
|
||||||
|
g_free(new_parsed_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar *stripped_file_name = g_strstrip(trimmed_file_name); // Strip leading and trailing whitespace
|
||||||
|
g_free(trimmed_file_name);
|
||||||
|
return stripped_file_name;
|
||||||
|
}
|
25
src/koto-utils.h
Normal file
25
src/koto-utils.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/* koto-utils.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.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
gchar* koto_utils_get_filename_without_extension(gchar *filename);
|
||||||
|
|
||||||
|
G_END_DECLS
|
|
@ -1,4 +1,6 @@
|
||||||
koto_sources = [
|
koto_sources = [
|
||||||
|
'indexer/album.c',
|
||||||
|
'indexer/artist.c',
|
||||||
'indexer/file.c',
|
'indexer/file.c',
|
||||||
'indexer/file-indexer.c',
|
'indexer/file-indexer.c',
|
||||||
'main.c',
|
'main.c',
|
||||||
|
@ -7,6 +9,7 @@ koto_sources = [
|
||||||
'koto-headerbar.c',
|
'koto-headerbar.c',
|
||||||
'koto-nav.c',
|
'koto-nav.c',
|
||||||
'koto-playerbar.c',
|
'koto-playerbar.c',
|
||||||
|
'koto-utils.c',
|
||||||
'koto-window.c',
|
'koto-window.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -15,6 +18,7 @@ koto_deps = [
|
||||||
dependency('gio-2.0', version: '>= 2.66'),
|
dependency('gio-2.0', version: '>= 2.66'),
|
||||||
dependency('gtk+-3.0', version: '>= 3.24'),
|
dependency('gtk+-3.0', version: '>= 3.24'),
|
||||||
dependency('libmagic', version: '>=5.39'),
|
dependency('libmagic', version: '>=5.39'),
|
||||||
|
dependency('taglib_c', version: '>=1.11'),
|
||||||
]
|
]
|
||||||
|
|
||||||
gnome = import('gnome')
|
gnome = import('gnome')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue