Implemented clearer database functions and response codes so we know when to do library indexing.
Implement reading of artists, albums, and tracks. Fixed a bug related to unnecessary file name parsing. Fix SIGSEGV related to file name parsing and koto_utils_get_filename_without_extension. Implemented an unquote string function for use when reading database entries. Try to fix some weird random rendering issues. Changed where we are doing some KotoPageMusicLocal widget construction.
This commit is contained in:
parent
7566fb39f9
commit
35762ca233
12 changed files with 346 additions and 167 deletions
63
src/db/db.c
63
src/db/db.c
|
@ -22,13 +22,19 @@
|
|||
#include <unistd.h>
|
||||
#include "db.h"
|
||||
|
||||
int KOTO_DB_SUCCESS = 0;
|
||||
int KOTO_DB_NEW = 1;
|
||||
int KOTO_DB_FAIL = 2;
|
||||
|
||||
sqlite3 *koto_db = NULL;
|
||||
gchar *db_filepath = NULL;
|
||||
gboolean created_new_db = FALSE;
|
||||
|
||||
void close_db() {
|
||||
sqlite3_close(koto_db);
|
||||
}
|
||||
|
||||
void create_db_tables() {
|
||||
int create_db_tables() {
|
||||
char *tables_creation_queries = "CREATE TABLE IF NOT EXISTS artists(id string UNIQUE PRIMARY KEY, path string, type int, name string, art_path string);"
|
||||
"CREATE TABLE IF NOT EXISTS albums(id string UNIQUE PRIMARY KEY, path string, artist_id string, name string, art_path string, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);"
|
||||
"CREATE TABLE IF NOT EXISTS tracks(id string UNIQUE PRIMARY KEY, path string, type int, artist_id string, album_id string, file_name string, name string, disc int, position int, FOREIGN KEY(artist_id) REFERENCES artists(id) ON DELETE CASCADE);";
|
||||
|
@ -39,9 +45,11 @@ void create_db_tables() {
|
|||
if (rc != SQLITE_OK) {
|
||||
g_critical("Failed to create required tables: %s", create_tables_errmsg);
|
||||
}
|
||||
|
||||
return (rc == SQLITE_OK) ? KOTO_DB_SUCCESS : KOTO_DB_FAIL;
|
||||
}
|
||||
|
||||
void enable_foreign_keys() {
|
||||
int enable_foreign_keys() {
|
||||
gchar *enable_foreign_keys_err = NULL;
|
||||
int rc = sqlite3_exec(koto_db, "PRAGMA foreign_keys = ON;", 0, 0, &enable_foreign_keys_err);
|
||||
|
||||
|
@ -50,24 +58,55 @@ void enable_foreign_keys() {
|
|||
}
|
||||
|
||||
g_free(enable_foreign_keys_err);
|
||||
return (rc == SQLITE_OK) ? KOTO_DB_SUCCESS : KOTO_DB_FAIL;
|
||||
}
|
||||
|
||||
void open_db() {
|
||||
gchar* get_db_path() {
|
||||
if (db_filepath == NULL) {
|
||||
const gchar *data_home = g_get_user_data_dir();
|
||||
gchar *data_dir = g_build_path(G_DIR_SEPARATOR_S, data_home, "com.github.joshstrobl.koto", NULL);
|
||||
db_filepath = g_build_filename(g_strdup(data_dir), "db", NULL); // Build out our path using XDG_DATA_HOME (e.g. .local/share/) + our namespace + db as the file name
|
||||
g_free(data_dir);
|
||||
}
|
||||
|
||||
return db_filepath;
|
||||
}
|
||||
|
||||
int have_existing_db() {
|
||||
struct stat db_stat;
|
||||
int success = stat(get_db_path(), &db_stat);
|
||||
|
||||
return ((success == 0) && S_ISREG(db_stat.st_mode)) ? 0 : 1;
|
||||
}
|
||||
|
||||
int open_db() {
|
||||
int ret = KOTO_DB_SUCCESS; // Default to last return being SUCCESS
|
||||
|
||||
if (have_existing_db() == 1) { // If we do not have an existing DB
|
||||
const gchar *data_home = g_get_user_data_dir();
|
||||
const gchar *data_dir = g_path_get_dirname(db_filepath);
|
||||
mkdir(data_home, 0755);
|
||||
mkdir(data_dir, 0755);
|
||||
chown(data_dir, getuid(), getgid());
|
||||
|
||||
gchar *db_path = g_build_filename(data_dir, "db", NULL); // Build out our path using XDG_DATA_HOME (e.g. .local/share/) + our namespace + db as the file name
|
||||
int rc = sqlite3_open(db_path, &koto_db);
|
||||
|
||||
if (rc) {
|
||||
g_critical("Failed to open or create database: %s", sqlite3_errmsg(koto_db));
|
||||
return;
|
||||
ret = KOTO_DB_NEW;
|
||||
}
|
||||
|
||||
enable_foreign_keys(); // Enable FOREIGN KEY support
|
||||
if (sqlite3_open(db_filepath, &koto_db) != KOTO_DB_SUCCESS) { // If we failed to open the database file
|
||||
g_critical("Failed to open or create database: %s", sqlite3_errmsg(koto_db));
|
||||
return KOTO_DB_FAIL;
|
||||
}
|
||||
|
||||
create_db_tables(); // Attempt to create our database tables
|
||||
if (enable_foreign_keys() != KOTO_DB_SUCCESS) { // If we failed to enable foreign keys
|
||||
return KOTO_DB_FAIL;
|
||||
}
|
||||
|
||||
if (create_db_tables() != KOTO_DB_SUCCESS) {// Failed to create our database tables
|
||||
return KOTO_DB_FAIL;
|
||||
}
|
||||
|
||||
if (ret == KOTO_DB_NEW) {
|
||||
created_new_db = TRUE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
14
src/db/db.h
14
src/db/db.h
|
@ -15,9 +15,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
void close_db();
|
||||
void enable_foreign_keys();
|
||||
void open_db();
|
||||
extern int KOTO_DB_SUCCESS;
|
||||
extern int KOTO_DB_NEW;
|
||||
extern int KOTO_DB_FAIL;
|
||||
extern gboolean created_new_db;
|
||||
|
||||
void close_db();
|
||||
int create_db_tables();
|
||||
gchar* get_db_path();
|
||||
int enable_foreign_keys();
|
||||
int have_existing_db();
|
||||
int open_db();
|
||||
|
|
|
@ -156,8 +156,6 @@ void koto_indexed_album_commit(KotoIndexedAlbum *self) {
|
|||
self->art_path
|
||||
);
|
||||
|
||||
g_debug("INSERT query for album: %s", commit_op);
|
||||
|
||||
gchar *commit_op_errmsg = NULL;
|
||||
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
|
||||
|
||||
|
@ -372,7 +370,6 @@ void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album
|
|||
|
||||
self->art_path = g_strdup(album_art);
|
||||
|
||||
g_message("Set album art to %s", album_art);
|
||||
self->has_album_art = TRUE;
|
||||
}
|
||||
|
||||
|
@ -478,7 +475,7 @@ KotoIndexedAlbum* koto_indexed_album_new(KotoIndexedArtist *artist, const gchar
|
|||
KotoIndexedAlbum* koto_indexed_album_new_with_uuid(KotoIndexedArtist *artist, const gchar *uuid) {
|
||||
return g_object_new(KOTO_TYPE_INDEXED_ALBUM,
|
||||
"artist", artist,
|
||||
"uuid", uuid,
|
||||
"uuid", g_strdup(uuid),
|
||||
"do-initial-index", FALSE,
|
||||
NULL
|
||||
);
|
||||
|
|
|
@ -95,8 +95,6 @@ void koto_indexed_artist_commit(KotoIndexedArtist *self) {
|
|||
self->artist_name
|
||||
);
|
||||
|
||||
g_debug("INSERT query for artist: %s", commit_op);
|
||||
|
||||
gchar *commit_opt_errmsg = NULL;
|
||||
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_opt_errmsg);
|
||||
|
||||
|
@ -161,20 +159,20 @@ void koto_indexed_artist_add_album(KotoIndexedArtist *self, KotoIndexedAlbum *al
|
|||
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);
|
||||
gchar *album_uuid;
|
||||
g_object_get(album, "uuid", &album_uuid, NULL);
|
||||
|
||||
if (album_name == NULL) {
|
||||
g_free(album_name);
|
||||
if (album_uuid == NULL) {
|
||||
g_free(album_uuid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_hash_table_contains(self->albums, album_name)) { // If we have this album already
|
||||
g_free(album_name);
|
||||
if (g_hash_table_contains(self->albums, album_uuid)) { // If we have this album already
|
||||
g_free(album_uuid);
|
||||
return;
|
||||
}
|
||||
|
||||
g_hash_table_insert(self->albums, album_name, album); // Add the album
|
||||
g_hash_table_insert(self->albums, album_uuid, album); // Add the album
|
||||
}
|
||||
|
||||
GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self) {
|
||||
|
@ -185,6 +183,14 @@ GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self) {
|
|||
return g_hash_table_get_values(self->albums);
|
||||
}
|
||||
|
||||
KotoIndexedAlbum* koto_indexed_artist_get_album(KotoIndexedArtist *self, gchar *album_uuid) {
|
||||
if (self->albums == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_hash_table_lookup(self->albums, album_uuid);
|
||||
}
|
||||
|
||||
void koto_indexed_artist_remove_album(KotoIndexedArtist *self, KotoIndexedAlbum *album) {
|
||||
if (album == NULL) { // No album defined
|
||||
return;
|
||||
|
@ -194,19 +200,10 @@ void koto_indexed_artist_remove_album(KotoIndexedArtist *self, KotoIndexedAlbum
|
|||
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);
|
||||
gchar *album_uuid;
|
||||
g_object_get(album, "uuid", &album_uuid, 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);
|
||||
g_hash_table_remove(self->albums, album_uuid);
|
||||
}
|
||||
|
||||
void koto_indexed_artist_update_path(KotoIndexedArtist *self, const gchar *new_path) {
|
||||
|
@ -249,7 +246,7 @@ KotoIndexedArtist* koto_indexed_artist_new(const gchar *path) {
|
|||
|
||||
KotoIndexedArtist* koto_indexed_artist_new_with_uuid(const gchar *uuid) {
|
||||
return g_object_new(KOTO_TYPE_INDEXED_ARTIST,
|
||||
"uuid", uuid,
|
||||
"uuid", g_strdup(uuid),
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,11 @@
|
|||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <taglib/tag_c.h>
|
||||
#include "../db/db.h"
|
||||
#include "structs.h"
|
||||
#include "../koto-utils.h"
|
||||
|
||||
extern sqlite3 *koto_db;
|
||||
|
||||
struct _KotoIndexedLibrary {
|
||||
GObject parent_instance;
|
||||
|
@ -79,7 +83,7 @@ void koto_indexed_library_add_artist(KotoIndexedLibrary *self, KotoIndexedArtist
|
|||
return;
|
||||
}
|
||||
|
||||
g_hash_table_insert(self->music_artists, artist_name, artist); // Add the artist
|
||||
g_hash_table_insert(self->music_artists, artist_name, artist); // Add the artist by its name (this needs to be done so we can get the artist when doing the depth of 2 indexing for the album)
|
||||
}
|
||||
|
||||
KotoIndexedArtist* koto_indexed_library_get_artist(KotoIndexedLibrary *self, gchar *artist_name) {
|
||||
|
@ -131,8 +135,7 @@ 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));
|
||||
start_indexing(self);
|
||||
koto_indexed_library_set_path(self, g_strdup(g_value_get_string(val)));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
||||
|
@ -140,6 +143,140 @@ static void koto_indexed_library_set_property(GObject *obj, guint prop_id, const
|
|||
}
|
||||
}
|
||||
|
||||
void koto_indexed_library_set_path(KotoIndexedLibrary *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(self); // Read from the database
|
||||
}
|
||||
}
|
||||
|
||||
int process_artists(void *data, int num_columns, char **fields, char **column_names) {
|
||||
(void) num_columns; (void) column_names; // Don't need these
|
||||
|
||||
KotoIndexedLibrary *self = (KotoIndexedLibrary*) data;
|
||||
|
||||
gchar *artist_uuid = g_strdup(koto_utils_unquote_string(fields[0])); // First column is UUID
|
||||
gchar *artist_path = g_strdup(koto_utils_unquote_string(fields[1])); // Second column is path
|
||||
gchar *artist_name = g_strdup(koto_utils_unquote_string(fields[3])); // Fourth column is artist name
|
||||
|
||||
KotoIndexedArtist *artist = koto_indexed_artist_new_with_uuid(artist_uuid); // Create our artist with the UUID
|
||||
|
||||
g_object_set(artist,
|
||||
"path", artist_path, // Set path
|
||||
"name", artist_name, // Set name
|
||||
NULL);
|
||||
|
||||
koto_indexed_library_add_artist(self, artist); // Add the artist
|
||||
|
||||
int albums_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM albums WHERE artist_id=\"%s\"", artist_uuid), process_albums, artist, NULL); // Process our albums
|
||||
if (albums_rc != SQLITE_OK) { // Failed to get our albums
|
||||
g_critical("Failed to read our albums: %s", sqlite3_errmsg(koto_db));
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_free(artist_uuid);
|
||||
g_free(artist_path);
|
||||
g_free(artist_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int process_albums(void *data, int num_columns, char **fields, char **column_names) {
|
||||
(void) num_columns; (void) column_names; // Don't need these
|
||||
|
||||
KotoIndexedArtist *artist = (KotoIndexedArtist*) data;
|
||||
|
||||
gchar *album_uuid = g_strdup(koto_utils_unquote_string(fields[0]));
|
||||
gchar *path = g_strdup(koto_utils_unquote_string(fields[1]));
|
||||
gchar *artist_uuid = g_strdup(koto_utils_unquote_string(fields[2]));
|
||||
gchar *album_name = g_strdup(koto_utils_unquote_string(fields[3]));
|
||||
gchar *album_art = (fields[4] != NULL) ? g_strdup(koto_utils_unquote_string(fields[4])) : NULL;
|
||||
|
||||
KotoIndexedAlbum *album = koto_indexed_album_new_with_uuid(artist, album_uuid); // Create our album
|
||||
|
||||
g_object_set(album,
|
||||
"path", path, // Set the path
|
||||
"name", album_name, // Set name
|
||||
"art-path", album_art, // Set art path if any
|
||||
NULL);
|
||||
|
||||
koto_indexed_artist_add_album(artist, album); // Add the album
|
||||
|
||||
int tracks_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM tracks WHERE album_id=\"%s\"", album_uuid), process_tracks, album, NULL); // Process our tracks
|
||||
if (tracks_rc != SQLITE_OK) { // Failed to get our tracks
|
||||
g_critical("Failed to read our tracks: %s", sqlite3_errmsg(koto_db));
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_free(album_uuid);
|
||||
g_free(path);
|
||||
g_free(artist_uuid);
|
||||
g_free(album_name);
|
||||
|
||||
if (album_art != NULL) {
|
||||
g_free(album_art);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int process_tracks(void *data, int num_columns, char **fields, char **column_names) {
|
||||
(void) num_columns; (void) column_names; // Don't need these
|
||||
|
||||
KotoIndexedAlbum *album = (KotoIndexedAlbum*) data;
|
||||
gchar *track_uuid = g_strdup(koto_utils_unquote_string(fields[0]));
|
||||
gchar *path = g_strdup(koto_utils_unquote_string(fields[1]));
|
||||
gchar *artist_uuid = g_strdup(koto_utils_unquote_string(fields[3]));
|
||||
gchar *album_uuid = g_strdup(koto_utils_unquote_string(fields[4]));
|
||||
gchar *file_name = g_strdup(koto_utils_unquote_string(fields[5]));
|
||||
gchar *name = g_strdup(koto_utils_unquote_string(fields[6]));
|
||||
guint *disc_num = (guint*) g_ascii_strtoull(fields[6], NULL, 10);
|
||||
guint *position = (guint*) g_ascii_strtoull(fields[7], NULL, 10);
|
||||
|
||||
KotoIndexedFile *file = koto_indexed_file_new_with_uuid(track_uuid); // Create our file
|
||||
g_object_set(file,
|
||||
"artist-uuid", artist_uuid,
|
||||
"album-uuid", album_uuid,
|
||||
"path", path,
|
||||
"file-name", file_name,
|
||||
"parsed-name", name,
|
||||
"cd", disc_num,
|
||||
"position", position,
|
||||
NULL);
|
||||
|
||||
koto_indexed_album_add_file(album, file); // Add the file
|
||||
|
||||
g_free(track_uuid);
|
||||
g_free(path);
|
||||
g_free(artist_uuid);
|
||||
g_free(album_uuid);
|
||||
g_free(file_name);
|
||||
g_free(name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void read_from_db(KotoIndexedLibrary *self) {
|
||||
int artists_rc = sqlite3_exec(koto_db, "SELECT * FROM artists", process_artists, self, NULL); // Process our artists
|
||||
if (artists_rc != SQLITE_OK) { // Failed to get our artists
|
||||
g_critical("Failed to read our artists: %s", sqlite3_errmsg(koto_db));
|
||||
return;
|
||||
}
|
||||
|
||||
g_hash_table_foreach(self->music_artists, output_artists, NULL);
|
||||
}
|
||||
|
||||
void start_indexing(KotoIndexedLibrary *self) {
|
||||
struct stat library_stat;
|
||||
int success = stat(self->path, &library_stat);
|
||||
|
@ -169,6 +306,13 @@ void start_indexing(KotoIndexedLibrary *self) {
|
|||
g_hash_table_foreach(self->music_artists, output_artists, NULL);
|
||||
}
|
||||
|
||||
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path) {
|
||||
return g_object_new(KOTO_TYPE_INDEXED_LIBRARY,
|
||||
"path", path,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth) {
|
||||
depth++;
|
||||
|
||||
|
@ -228,8 +372,8 @@ 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);
|
||||
|
||||
g_debug("Artist: %s", artist_name);
|
||||
|
||||
GList *albums = koto_indexed_artist_get_albums(artist); // Get the albums for this artist
|
||||
|
||||
if (albums != NULL) {
|
||||
|
@ -267,10 +411,3 @@ void output_artists(gpointer artist_key, gpointer artist_ptr, gpointer data) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path) {
|
||||
return g_object_new(KOTO_TYPE_INDEXED_LIBRARY,
|
||||
"path", path,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
|
|
@ -49,9 +49,9 @@ enum {
|
|||
PROP_ALBUM_UUID,
|
||||
PROP_UUID,
|
||||
PROP_DO_INITIAL_INDEX,
|
||||
PROP_PATH,
|
||||
PROP_ARTIST,
|
||||
PROP_ALBUM,
|
||||
PROP_PATH,
|
||||
PROP_FILE_NAME,
|
||||
PROP_PARSED_NAME,
|
||||
PROP_CD,
|
||||
|
@ -182,15 +182,15 @@ static void koto_indexed_file_get_property(GObject *obj, guint prop_id, GValue *
|
|||
case PROP_UUID:
|
||||
g_value_set_string(val, self->uuid);
|
||||
break;
|
||||
case PROP_PATH:
|
||||
g_value_set_string(val, self->path);
|
||||
break;
|
||||
case PROP_ARTIST:
|
||||
g_value_set_string(val, self->artist);
|
||||
break;
|
||||
case PROP_ALBUM:
|
||||
g_value_set_string(val, self->album);
|
||||
break;
|
||||
case PROP_PATH:
|
||||
g_value_set_string(val, self->path);
|
||||
break;
|
||||
case PROP_FILE_NAME:
|
||||
g_value_set_string(val, self->file_name);
|
||||
break;
|
||||
|
@ -228,15 +228,15 @@ static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GV
|
|||
case PROP_DO_INITIAL_INDEX:
|
||||
self->do_initial_index = g_value_get_boolean(val);
|
||||
break;
|
||||
case PROP_PATH:
|
||||
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));
|
||||
break;
|
||||
case PROP_ALBUM:
|
||||
self->album = g_strdup(g_value_get_string(val));
|
||||
break;
|
||||
case PROP_PATH:
|
||||
koto_indexed_file_update_path(self, g_value_get_string(val)); // Update the path
|
||||
break;
|
||||
case PROP_FILE_NAME:
|
||||
koto_indexed_file_set_file_name(self, g_strdup(g_value_get_string(val))); // Update the file name
|
||||
break;
|
||||
|
@ -279,8 +279,6 @@ void koto_indexed_file_commit(KotoIndexedFile *self) {
|
|||
GPOINTER_TO_INT((int*) self->position)
|
||||
);
|
||||
|
||||
g_debug("INSERT query for file: %s", commit_op);
|
||||
|
||||
gchar *commit_op_errmsg = NULL;
|
||||
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
|
||||
|
||||
|
@ -299,34 +297,16 @@ void koto_indexed_file_parse_name(KotoIndexedFile *self) {
|
|||
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
|
||||
split = g_strsplit(copied_file_name, g_utf8_strdown(self->artist, -1), -1); // Lowercase album name and split by that
|
||||
copied_file_name = g_strjoinv("", split); // Remove the artist
|
||||
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);
|
||||
gchar **split = g_regex_split_simple("^([\\d]+)", 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];
|
||||
|
@ -347,6 +327,14 @@ void koto_indexed_file_parse_name(KotoIndexedFile *self) {
|
|||
|
||||
g_strfreev(split);
|
||||
|
||||
split = g_strsplit(file_without_ext, " - ", -1); // Split whenever we encounter " - "
|
||||
file_without_ext = g_strjoinv("", split); // Remove entirely
|
||||
g_strfreev(split);
|
||||
|
||||
split = g_strsplit(file_without_ext, "-", -1); // Split whenever we encounter -
|
||||
file_without_ext = g_strjoinv("", split); // Remove entirely
|
||||
g_strfreev(split);
|
||||
|
||||
koto_indexed_file_set_parsed_name(self, file_without_ext);
|
||||
g_free(file_without_ext);
|
||||
}
|
||||
|
@ -367,7 +355,7 @@ void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name
|
|||
self->file_name = g_strdup(new_file_name);
|
||||
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_FILE_NAME]);
|
||||
|
||||
if (!self->acquired_metadata_from_id3) { // Haven't acquired our information from ID3
|
||||
if (!self->acquired_metadata_from_id3 && self->do_initial_index) { // Haven't acquired our information from ID3
|
||||
koto_indexed_file_parse_name(self); // Update our parsed name
|
||||
}
|
||||
}
|
||||
|
@ -417,6 +405,8 @@ void koto_indexed_file_update_metadata(KotoIndexedFile *self) {
|
|||
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
|
||||
} else {
|
||||
koto_indexed_file_set_file_name(self, g_path_get_basename(self->path)); // Update our file name
|
||||
}
|
||||
|
||||
taglib_tag_free_strings(); // Free strings
|
||||
|
@ -433,8 +423,10 @@ void koto_indexed_file_update_path(KotoIndexedFile *self, const gchar *new_path)
|
|||
}
|
||||
|
||||
self->path = g_strdup(new_path); // Duplicate the path and set it
|
||||
|
||||
if (self->do_initial_index) {
|
||||
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]);
|
||||
}
|
||||
|
@ -443,15 +435,27 @@ KotoIndexedFile* koto_indexed_file_new(KotoIndexedAlbum *album, const gchar *pat
|
|||
KotoIndexedArtist *artist;
|
||||
|
||||
gchar *artist_uuid;
|
||||
gchar *artist_name;
|
||||
gchar *album_uuid;
|
||||
gchar *album_name;
|
||||
|
||||
g_object_get(album, "artist", &artist, NULL); // Get the artist from our Album
|
||||
g_object_get(artist, "uuid", &artist_uuid, NULL); // Get the artist UUID from the artist
|
||||
g_object_get(album, "uuid", &album_uuid, NULL); // Get the album UUID from the album
|
||||
g_object_get(artist,
|
||||
"name", &artist_name,
|
||||
"uuid", &artist_uuid, // Get the artist UUID from the artist
|
||||
NULL);
|
||||
|
||||
g_object_get(album,
|
||||
"name", &album_name,
|
||||
"uuid", &album_uuid, // Get the album UUID from the album
|
||||
NULL);
|
||||
|
||||
KotoIndexedFile *file = g_object_new(KOTO_TYPE_INDEXED_FILE,
|
||||
"artist-uuid", artist_uuid,
|
||||
"album-uuid", album_uuid,
|
||||
"artist", artist_name,
|
||||
"album", album_name,
|
||||
"do-initial-index", TRUE,
|
||||
"uuid", g_uuid_string_random(),
|
||||
"path", path,
|
||||
"cd", cd,
|
||||
|
@ -464,7 +468,7 @@ KotoIndexedFile* koto_indexed_file_new(KotoIndexedAlbum *album, const gchar *pat
|
|||
|
||||
KotoIndexedFile* koto_indexed_file_new_with_uuid(const gchar *uuid) {
|
||||
return g_object_new(KOTO_TYPE_INDEXED_FILE,
|
||||
"uuid", uuid,
|
||||
"uuid", g_strdup(uuid),
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,11 @@ void koto_indexed_library_add_artist(KotoIndexedLibrary *self, KotoIndexedArtist
|
|||
KotoIndexedArtist* koto_indexed_library_get_artist(KotoIndexedLibrary *self, gchar* artist_name);
|
||||
GHashTable* koto_indexed_library_get_artists(KotoIndexedLibrary *self);
|
||||
void koto_indexed_library_remove_artist(KotoIndexedLibrary *self, KotoIndexedArtist *artist);
|
||||
void koto_indexed_library_set_path(KotoIndexedLibrary *self, gchar *path);
|
||||
int process_artists(void *data, int num_columns, char **fields, char **column_names);
|
||||
int process_albums(void *data, int num_columns, char **fields, char **column_names);
|
||||
int process_tracks(void *data, int num_columns, char **fields, char **column_names);
|
||||
void read_from_db(KotoIndexedLibrary *self);
|
||||
void start_indexing(KotoIndexedLibrary *self);
|
||||
void index_folder(KotoIndexedLibrary *self, gchar *path, guint depth);
|
||||
|
||||
|
@ -61,6 +66,7 @@ void koto_indexed_artist_add_album(KotoIndexedArtist *self, KotoIndexedAlbum *al
|
|||
void koto_indexed_artist_commit(KotoIndexedArtist *self);
|
||||
guint koto_indexed_artist_find_album_with_name(gconstpointer *album_data, gconstpointer *album_name_data);
|
||||
GList* koto_indexed_artist_get_albums(KotoIndexedArtist *self);
|
||||
KotoIndexedAlbum* koto_indexed_artist_get_album(KotoIndexedArtist *self, gchar *album_uuid);
|
||||
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);
|
||||
|
|
|
@ -54,7 +54,7 @@ gchar* koto_utils_get_filename_without_extension(gchar *filename) {
|
|||
} 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
|
||||
new_parsed_name = 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
|
||||
}
|
||||
}
|
||||
|
@ -67,3 +67,19 @@ gchar* koto_utils_get_filename_without_extension(gchar *filename) {
|
|||
g_free(trimmed_file_name);
|
||||
return stripped_file_name;
|
||||
}
|
||||
|
||||
gchar* koto_utils_unquote_string(gchar *s) {
|
||||
gchar *new_s = NULL;
|
||||
|
||||
if (g_str_has_prefix(s, "'") && g_str_has_suffix(s, "'")) { // Begins and ends with '
|
||||
new_s = g_utf8_substring(s, 1, g_utf8_strlen(s, -1)-1); // Start at 1 and end at n-1
|
||||
} else {
|
||||
new_s = g_strdup(s);
|
||||
}
|
||||
|
||||
gchar **split_on_double_single = g_strsplit(new_s, "''", -1); // Split on instances of ''
|
||||
new_s = g_strjoinv("'", split_on_double_single); // Rejoin as '
|
||||
g_strfreev(split_on_double_single); // Free our array
|
||||
|
||||
return new_s;
|
||||
}
|
||||
|
|
|
@ -23,5 +23,6 @@ G_BEGIN_DECLS
|
|||
|
||||
GtkWidget* koto_utils_create_image_from_filepath(gchar *filepath, gchar *fallback_icon, guint width, guint height);
|
||||
gchar* koto_utils_get_filename_without_extension(gchar *filename);
|
||||
gchar* koto_utils_unquote_string(gchar *s);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
|
@ -73,6 +73,8 @@ static void koto_window_init (KotoWindow *self) {
|
|||
if (GTK_IS_STACK(self->pages)) { // Created our stack successfully
|
||||
gtk_stack_set_transition_type(GTK_STACK(self->pages), GTK_STACK_TRANSITION_TYPE_OVER_LEFT_RIGHT);
|
||||
gtk_box_append(GTK_BOX(self->content_layout), self->pages);
|
||||
gtk_widget_set_hexpand(self->pages, TRUE);
|
||||
gtk_widget_set_vexpand(self->pages, TRUE);
|
||||
}
|
||||
|
||||
gtk_box_prepend(GTK_BOX(self->primary_layout), self->content_layout);
|
||||
|
@ -87,13 +89,12 @@ static void koto_window_init (KotoWindow *self) {
|
|||
gtk_window_set_child(GTK_WINDOW(self), self->primary_layout);
|
||||
#ifdef GDK_WINDOWING_X11
|
||||
set_optimal_default_window_size(self);
|
||||
#else
|
||||
gtk_widget_set_size_request(GTK_WIDGET(self), 1200, 675);
|
||||
#endif
|
||||
gtk_window_set_title(GTK_WINDOW(self), "Koto");
|
||||
gtk_window_set_icon_name(GTK_WINDOW(self), "audio-headphones");
|
||||
gtk_window_set_startup_id(GTK_WINDOW(self), "com.github.joshstrobl.koto");
|
||||
|
||||
gtk_widget_queue_draw(self->content_layout);
|
||||
g_thread_new("load-library", (void*) load_library, self);
|
||||
}
|
||||
|
||||
|
@ -124,13 +125,14 @@ void load_library(KotoWindow *self) {
|
|||
self->library = lib;
|
||||
KotoPageMusicLocal* l = koto_page_music_local_new();
|
||||
|
||||
if (GTK_IS_WIDGET(l)) { // Created our local library page
|
||||
koto_page_music_local_set_library(l, self->library);
|
||||
gtk_stack_add_named(GTK_STACK(self->pages), GTK_WIDGET(l), "music.local");
|
||||
// TODO: Remove and do some fancy state loading
|
||||
gtk_stack_add_named(GTK_STACK(self->pages), GTK_WIDGET(l), "music.local");
|
||||
gtk_stack_set_visible_child_name(GTK_STACK(self->pages), "music.local");
|
||||
gtk_widget_show(self->pages); // Do not remove this. Will cause sporadic hiding of the local page content otherwise.
|
||||
koto_page_music_local_set_library(l, self->library);
|
||||
}
|
||||
}
|
||||
|
||||
g_thread_exit(0);
|
||||
}
|
||||
|
||||
void set_optimal_default_window_size(KotoWindow *self) {
|
||||
|
|
|
@ -30,7 +30,7 @@ static void on_activate (GtkApplication *app) {
|
|||
|
||||
window = gtk_application_get_active_window (app);
|
||||
if (window == NULL) {
|
||||
window = g_object_new(KOTO_TYPE_WINDOW, "application", app, "default-width", 600, "default-height", 300, NULL);
|
||||
window = g_object_new(KOTO_TYPE_WINDOW, "application", app, "default-width", 1200, "default-height", 675, NULL);
|
||||
}
|
||||
|
||||
gtk_window_present(window);
|
||||
|
@ -38,11 +38,6 @@ static void on_activate (GtkApplication *app) {
|
|||
|
||||
static void on_shutdown(GtkApplication *app) {
|
||||
(void) app;
|
||||
|
||||
if (koto_db != NULL) {
|
||||
g_message("Have a db?");
|
||||
}
|
||||
|
||||
close_db(); // Close the database
|
||||
}
|
||||
|
||||
|
|
|
@ -97,12 +97,35 @@ static void koto_page_music_local_set_property(GObject *obj, guint prop_id, cons
|
|||
static void koto_page_music_local_init(KotoPageMusicLocal *self) {
|
||||
self->lib = NULL;
|
||||
self->constructed = FALSE;
|
||||
|
||||
gtk_widget_add_css_class(GTK_WIDGET(self), "page-music-local");
|
||||
gtk_widget_set_hexpand(GTK_WIDGET(self), TRUE);
|
||||
gtk_widget_set_vexpand(GTK_WIDGET(self), TRUE);
|
||||
|
||||
self->scrolled_window = gtk_scrolled_window_new();
|
||||
gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(self->scrolled_window), 300);
|
||||
gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(self->scrolled_window), FALSE);
|
||||
gtk_widget_add_css_class(self->scrolled_window, "artist-list");
|
||||
gtk_widget_set_vexpand(self->scrolled_window, TRUE); // Expand our scrolled window
|
||||
gtk_box_prepend(GTK_BOX(self), self->scrolled_window);
|
||||
|
||||
self->artist_list = gtk_list_box_new(); // Create our artist list
|
||||
g_signal_connect(GTK_LIST_BOX(self->artist_list), "row-activated", G_CALLBACK(koto_page_music_local_handle_artist_click), self);
|
||||
gtk_list_box_set_activate_on_single_click(GTK_LIST_BOX(self->artist_list), TRUE);
|
||||
gtk_list_box_set_selection_mode(GTK_LIST_BOX(self->artist_list), GTK_SELECTION_BROWSE);
|
||||
gtk_list_box_set_sort_func(GTK_LIST_BOX(self->artist_list), koto_page_music_local_sort_artists, NULL, NULL); // Add our sort function
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window), self->artist_list);
|
||||
|
||||
self->stack = gtk_stack_new(); // Create a new stack
|
||||
gtk_stack_set_transition_duration(GTK_STACK(self->stack), 400);
|
||||
gtk_stack_set_transition_type(GTK_STACK(self->stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
|
||||
gtk_widget_set_hexpand(self->stack, TRUE);
|
||||
gtk_widget_set_vexpand(self->stack, TRUE);
|
||||
gtk_box_append(GTK_BOX(self), self->stack);
|
||||
}
|
||||
|
||||
static void koto_page_music_local_constructed(GObject *obj) {
|
||||
KotoPageMusicLocal *self = KOTO_PAGE_MUSIC_LOCAL(obj);
|
||||
gtk_widget_add_css_class(GTK_WIDGET(self), "page-music-local");
|
||||
gtk_widget_set_hexpand(GTK_WIDGET(self), TRUE);
|
||||
|
||||
G_OBJECT_CLASS (koto_page_music_local_parent_class)->constructed (obj);
|
||||
self->constructed = TRUE;
|
||||
|
@ -144,45 +167,6 @@ void koto_page_music_local_set_library(KotoPageMusicLocal *self, KotoIndexedLibr
|
|||
return;
|
||||
}
|
||||
|
||||
if (!GTK_IS_SCROLLED_WINDOW(self->scrolled_window)) {
|
||||
self->scrolled_window = gtk_scrolled_window_new();
|
||||
gtk_widget_add_css_class(self->scrolled_window, "artist-list");
|
||||
gtk_box_prepend(GTK_BOX(self), self->scrolled_window);
|
||||
}
|
||||
|
||||
if (GTK_IS_LIST_BOX(self->artist_list)) { // artist list is a list box
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window), NULL); // Set to null to maybe clear?
|
||||
g_object_unref(self->artist_list); // Unref
|
||||
}
|
||||
|
||||
if (GTK_IS_STACK(self->stack)) { // Stack is a Stack
|
||||
gtk_box_remove(GTK_BOX(self), GTK_WIDGET(self->stack)); // Destroy to free references
|
||||
}
|
||||
|
||||
self->artist_list = gtk_list_box_new(); // Create our artist list
|
||||
|
||||
gboolean list_created = GTK_IS_LIST_BOX(self->artist_list);
|
||||
|
||||
if (list_created) { // Successfully created our list
|
||||
g_signal_connect(GTK_LIST_BOX(self->artist_list), "row-activated", G_CALLBACK(koto_page_music_local_handle_artist_click), self);
|
||||
gtk_list_box_set_activate_on_single_click(GTK_LIST_BOX(self->artist_list), TRUE);
|
||||
gtk_list_box_set_selection_mode(GTK_LIST_BOX(self->artist_list), GTK_SELECTION_BROWSE);
|
||||
gtk_list_box_set_sort_func(GTK_LIST_BOX(self->artist_list), koto_page_music_local_sort_artists, NULL, NULL); // Add our sort function
|
||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window), self->artist_list);
|
||||
gtk_widget_show(GTK_WIDGET(self->artist_list));
|
||||
}
|
||||
|
||||
gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(self->scrolled_window), 300);
|
||||
gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(self->scrolled_window), FALSE);
|
||||
gtk_widget_set_size_request(GTK_WIDGET(self->artist_list), 300, -1);
|
||||
|
||||
self->stack = gtk_stack_new(); // Create a new stack
|
||||
gtk_stack_set_transition_duration(GTK_STACK(self->stack), 400);
|
||||
gtk_stack_set_transition_type(GTK_STACK(self->stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
|
||||
gtk_widget_set_hexpand(self->stack, TRUE);
|
||||
gboolean stack_created = GTK_IS_STACK(self->stack);
|
||||
|
||||
if (list_created && stack_created) {
|
||||
GHashTableIter artist_list_iter;
|
||||
gpointer artist_key;
|
||||
gpointer artist_data;
|
||||
|
@ -194,13 +178,6 @@ void koto_page_music_local_set_library(KotoPageMusicLocal *self, KotoIndexedLibr
|
|||
KotoIndexedArtist *artist = (KotoIndexedArtist*) artist_data; // Cast our data as a KotoIndexedArtist
|
||||
koto_page_music_local_add_artist(self, artist);
|
||||
}
|
||||
}
|
||||
|
||||
if (stack_created) { // Successfully created our stack
|
||||
gtk_box_append(GTK_BOX(self), self->stack);
|
||||
}
|
||||
|
||||
gtk_widget_show(GTK_WIDGET(self));
|
||||
}
|
||||
|
||||
int koto_page_music_local_sort_artists(GtkListBoxRow *artist1, GtkListBoxRow *artist2, gpointer user_data) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue