2021-02-16 17:15:10 +02:00
|
|
|
/* 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>
|
2021-03-02 19:12:12 +02:00
|
|
|
#include <sqlite3.h>
|
2021-02-16 17:15:10 +02:00
|
|
|
#include <stdio.h>
|
2021-03-23 19:50:09 +02:00
|
|
|
#include "../db/cartographer.h"
|
2021-03-10 13:44:08 +02:00
|
|
|
#include "../playlist/current.h"
|
|
|
|
#include "../playlist/playlist.h"
|
2021-03-02 19:12:12 +02:00
|
|
|
#include "structs.h"
|
2021-02-16 17:15:10 +02:00
|
|
|
#include "koto-utils.h"
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
extern KotoCartographer * koto_maps;
|
|
|
|
extern KotoCurrentPlaylist * current_playlist;
|
|
|
|
extern sqlite3 * koto_db;
|
2021-03-02 19:12:12 +02:00
|
|
|
|
2021-02-16 17:15:10 +02:00
|
|
|
struct _KotoIndexedAlbum {
|
|
|
|
GObject parent_instance;
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * uuid;
|
|
|
|
gchar * path;
|
2021-02-16 17:15:10 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * name;
|
|
|
|
gchar * art_path;
|
|
|
|
gchar * artist_uuid;
|
|
|
|
GList * tracks;
|
2021-02-16 17:15:10 +02:00
|
|
|
|
|
|
|
gboolean has_album_art;
|
2021-03-02 19:12:12 +02:00
|
|
|
gboolean do_initial_index;
|
2021-02-16 17:15:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(KotoIndexedAlbum, koto_indexed_album, G_TYPE_OBJECT);
|
|
|
|
|
|
|
|
enum {
|
|
|
|
PROP_0,
|
2021-03-02 19:12:12 +02:00
|
|
|
PROP_UUID,
|
|
|
|
PROP_DO_INITIAL_INDEX,
|
2021-02-16 17:15:10 +02:00
|
|
|
PROP_PATH,
|
|
|
|
PROP_ALBUM_NAME,
|
|
|
|
PROP_ART_PATH,
|
2021-03-23 19:50:09 +02:00
|
|
|
PROP_ARTIST_UUID,
|
2021-02-16 17:15:10 +02:00
|
|
|
N_PROPERTIES
|
|
|
|
};
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
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;
|
2021-02-16 17:15:10 +02:00
|
|
|
|
|
|
|
|
|
|
|
gobject_class = G_OBJECT_CLASS(c);
|
|
|
|
gobject_class->set_property = koto_indexed_album_set_property;
|
|
|
|
gobject_class->get_property = koto_indexed_album_get_property;
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
props[PROP_UUID] = g_param_spec_string(
|
|
|
|
"uuid",
|
|
|
|
"UUID to Album in database",
|
|
|
|
"UUID to Album in database",
|
|
|
|
NULL,
|
2021-05-11 20:05:04 +03:00
|
|
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
2021-03-02 19:12:12 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
props[PROP_DO_INITIAL_INDEX] = g_param_spec_boolean(
|
|
|
|
"do-initial-index",
|
|
|
|
"Do an initial indexing operating instead of pulling from the database",
|
|
|
|
"Do an initial indexing operating instead of pulling from the database",
|
|
|
|
FALSE,
|
2021-05-11 20:05:04 +03:00
|
|
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
2021-03-02 19:12:12 +02:00
|
|
|
);
|
|
|
|
|
2021-02-16 17:15:10 +02:00
|
|
|
props[PROP_PATH] = g_param_spec_string(
|
|
|
|
"path",
|
|
|
|
"Path",
|
|
|
|
"Path to Album",
|
|
|
|
NULL,
|
2021-05-11 20:05:04 +03:00
|
|
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
2021-02-16 17:15:10 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
props[PROP_ALBUM_NAME] = g_param_spec_string(
|
|
|
|
"name",
|
|
|
|
"Name",
|
|
|
|
"Name of Album",
|
|
|
|
NULL,
|
2021-05-11 20:05:04 +03:00
|
|
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
2021-02-16 17:15:10 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
props[PROP_ART_PATH] = g_param_spec_string(
|
|
|
|
"art-path",
|
|
|
|
"Path to Artwork",
|
|
|
|
"Path to Artwork",
|
|
|
|
NULL,
|
2021-05-11 20:05:04 +03:00
|
|
|
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
2021-02-16 17:15:10 +02:00
|
|
|
);
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
props[PROP_ARTIST_UUID] = g_param_spec_string(
|
|
|
|
"artist-uuid",
|
|
|
|
"UUID of Artist associated with Album",
|
|
|
|
"UUID of Artist associated with Album",
|
|
|
|
NULL,
|
2021-05-11 20:05:04 +03:00
|
|
|
G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
|
2021-03-23 19:50:09 +02:00
|
|
|
);
|
|
|
|
|
2021-02-16 17:15:10 +02:00
|
|
|
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
static void koto_indexed_album_init(KotoIndexedAlbum * self) {
|
2021-02-16 17:15:10 +02:00
|
|
|
self->has_album_art = FALSE;
|
2021-03-23 19:50:09 +02:00
|
|
|
self->tracks = NULL;
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_add_track(
|
|
|
|
KotoIndexedAlbum * self,
|
|
|
|
KotoIndexedTrack * track
|
|
|
|
) {
|
2021-03-16 08:51:35 +02:00
|
|
|
if (track == NULL) { // Not a file
|
2021-02-16 17:15:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * track_uuid;
|
|
|
|
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
g_object_get(track, "uuid", &track_uuid, NULL);
|
2021-02-16 17:15:10 +02:00
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
if (g_list_index(self->tracks, track_uuid) == -1) {
|
|
|
|
self->tracks = g_list_insert_sorted_with_data(self->tracks, track_uuid, koto_indexed_album_sort_tracks, NULL);
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_commit(KotoIndexedAlbum * self) {
|
2021-03-02 19:12:12 +02:00
|
|
|
if (self->art_path == NULL) { // If art_path isn't defined when committing
|
|
|
|
koto_indexed_album_set_album_art(self, ""); // Set to an empty string
|
|
|
|
}
|
2021-02-16 17:15:10 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * commit_op = g_strdup_printf(
|
2021-03-02 19:12:12 +02:00
|
|
|
"INSERT INTO albums(id, path, artist_id, name, art_path)"
|
|
|
|
"VALUES('%s', quote(\"%s\"), '%s', quote(\"%s\"), quote(\"%s\"))"
|
|
|
|
"ON CONFLICT(id) DO UPDATE SET path=excluded.path, name=excluded.name, art_path=excluded.art_path;",
|
|
|
|
self->uuid,
|
|
|
|
self->path,
|
2021-03-23 19:50:09 +02:00
|
|
|
self->artist_uuid,
|
2021-03-02 19:12:12 +02:00
|
|
|
self->name,
|
|
|
|
self->art_path
|
|
|
|
);
|
2021-02-16 17:15:10 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * commit_op_errmsg = NULL;
|
2021-03-02 19:12:12 +02:00
|
|
|
int rc = sqlite3_exec(koto_db, commit_op, 0, 0, &commit_op_errmsg);
|
2021-02-16 17:15:10 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
if (rc != SQLITE_OK) {
|
|
|
|
g_warning("Failed to write our album to the database: %s", commit_op_errmsg);
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
g_free(commit_op);
|
|
|
|
g_free(commit_op_errmsg);
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_find_album_art(KotoIndexedAlbum * self) {
|
2021-03-02 19:12:12 +02:00
|
|
|
magic_t magic_cookie = magic_open(MAGIC_MIME);
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
if (magic_cookie == NULL) {
|
2021-02-16 17:15:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
if (magic_load(magic_cookie, NULL) != 0) {
|
|
|
|
magic_close(magic_cookie);
|
|
|
|
return;
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
DIR * dir = opendir(self->path); // Attempt to open our directory
|
|
|
|
|
2021-02-25 18:15:36 +02:00
|
|
|
|
|
|
|
if (dir == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
struct dirent * entry;
|
|
|
|
|
2021-02-25 18:15:36 +02:00
|
|
|
|
|
|
|
while ((entry = readdir(dir))) {
|
2021-03-02 19:12:12 +02:00
|
|
|
if (entry->d_type != DT_REG) { // Not a regular file
|
|
|
|
continue; // SKIP
|
2021-02-25 18:15:36 +02:00
|
|
|
}
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item
|
2021-02-25 18:15:36 +02:00
|
|
|
continue; // Skip
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * full_path = g_strdup_printf("%s%s%s", self->path, G_DIR_SEPARATOR_S, entry->d_name);
|
2021-03-02 19:12:12 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
const char * mime_type = magic_file(magic_cookie, full_path);
|
2021-02-25 18:15:36 +02:00
|
|
|
|
|
|
|
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
|
2021-05-11 20:05:04 +03:00
|
|
|
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
|
2021-02-25 18:15:36 +02:00
|
|
|
|
|
|
|
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);
|
2021-03-02 19:12:12 +02:00
|
|
|
g_free(album_art_no_ext);
|
|
|
|
g_free(lower_art);
|
|
|
|
break;
|
2021-02-25 18:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_free(album_art_no_ext);
|
|
|
|
g_free(lower_art);
|
2021-03-02 19:12:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_free(full_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
magic_close(magic_cookie);
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_find_tracks(
|
|
|
|
KotoIndexedAlbum * self,
|
|
|
|
magic_t magic_cookie,
|
|
|
|
const gchar * path
|
|
|
|
) {
|
2021-03-02 19:12:12 +02:00
|
|
|
if (magic_cookie == NULL) { // No cookie provided
|
|
|
|
magic_cookie = magic_open(MAGIC_MIME);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (magic_cookie == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path == NULL) {
|
|
|
|
path = self->path;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (magic_load(magic_cookie, NULL) != 0) {
|
|
|
|
magic_close(magic_cookie);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
DIR * dir = opendir(path); // Attempt to open our directory
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
|
|
|
|
if (dir == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
struct dirent * entry;
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
|
|
|
|
while ((entry = readdir(dir))) {
|
|
|
|
if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item
|
|
|
|
continue; // Skip
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name);
|
2021-03-02 19:12:12 +02:00
|
|
|
|
|
|
|
if (entry->d_type == DT_DIR) { // If this is a directory
|
|
|
|
koto_indexed_album_find_tracks(self, magic_cookie, full_path); // Recursively find tracks
|
|
|
|
g_free(full_path);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry->d_type != DT_REG) { // Not a regular file
|
|
|
|
continue; // SKIP
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
const char * mime_type = magic_file(magic_cookie, full_path);
|
2021-03-02 19:12:12 +02:00
|
|
|
|
|
|
|
if (mime_type == NULL) { // Failed to get the mimetype
|
|
|
|
g_free(full_path);
|
|
|
|
continue; // Skip
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_str_has_prefix(mime_type, "audio/") || g_str_has_prefix(mime_type, "video/ogg")) { // Is an audio file or ogg because it is special
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * appended_slash_to_path = g_strdup_printf("%s%s", g_strdup(self->path), G_DIR_SEPARATOR_S);
|
|
|
|
gchar ** possible_cd_split = g_strsplit(full_path, appended_slash_to_path, -1); // Split based on the album path
|
|
|
|
guint * cd = (guint*) 1;
|
2021-02-25 18:15:36 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * track_with_cd_sep = g_strdup(possible_cd_split[1]); // Duplicate
|
|
|
|
gchar ** split_on_cd = g_strsplit(track_with_cd_sep, G_DIR_SEPARATOR_S, -1); // Split based on separator (e.g. / )
|
2021-02-25 18:15:36 +02:00
|
|
|
|
|
|
|
if (g_strv_length(split_on_cd) > 1) {
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * cdd = g_strdup(split_on_cd[0]);
|
|
|
|
gchar ** cd_sep = g_strsplit(g_utf8_strdown(cdd, -1), "cd", -1);
|
2021-02-25 18:15:36 +02:00
|
|
|
|
|
|
|
if (g_strv_length(cd_sep) > 1) {
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * pos_str = g_strdup(cd_sep[1]);
|
2021-02-25 18:15:36 +02:00
|
|
|
cd = (guint*) g_ascii_strtoull(pos_str, NULL, 10); // Attempt to convert
|
|
|
|
g_free(pos_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_strfreev(cd_sep);
|
|
|
|
g_free(cdd);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_strfreev(split_on_cd);
|
2021-03-16 08:51:35 +02:00
|
|
|
g_free(track_with_cd_sep);
|
2021-02-25 18:15:36 +02:00
|
|
|
|
|
|
|
g_strfreev(possible_cd_split);
|
|
|
|
g_free(appended_slash_to_path);
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
KotoIndexedTrack * track = koto_indexed_track_new(self, full_path, cd);
|
2021-02-25 18:15:36 +02:00
|
|
|
|
2021-03-16 08:51:35 +02:00
|
|
|
if (track != NULL) { // Is a file
|
2021-03-23 19:50:09 +02:00
|
|
|
koto_indexed_album_add_track(self, track); // Add our file
|
2021-02-25 18:15:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(full_path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
static void koto_indexed_album_get_property(
|
|
|
|
GObject * obj,
|
|
|
|
guint prop_id,
|
|
|
|
GValue * val,
|
|
|
|
GParamSpec * spec
|
|
|
|
) {
|
|
|
|
KotoIndexedAlbum * self = KOTO_INDEXED_ALBUM(obj);
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
|
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_UUID:
|
|
|
|
g_value_set_string(val, self->uuid);
|
|
|
|
break;
|
|
|
|
case PROP_DO_INITIAL_INDEX:
|
|
|
|
g_value_set_boolean(val, self->do_initial_index);
|
|
|
|
break;
|
|
|
|
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;
|
2021-03-23 19:50:09 +02:00
|
|
|
case PROP_ARTIST_UUID:
|
|
|
|
g_value_set_string(val, self->artist_uuid);
|
|
|
|
break;
|
2021-03-02 19:12:12 +02:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
static void koto_indexed_album_set_property(
|
|
|
|
GObject * obj,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue * val,
|
|
|
|
GParamSpec * spec
|
|
|
|
) {
|
|
|
|
KotoIndexedAlbum * self = KOTO_INDEXED_ALBUM(obj);
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
switch (prop_id) {
|
|
|
|
case PROP_UUID:
|
|
|
|
self->uuid = g_strdup(g_value_get_string(val));
|
|
|
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_UUID]);
|
|
|
|
break;
|
|
|
|
case PROP_DO_INITIAL_INDEX:
|
|
|
|
self->do_initial_index = g_value_get_boolean(val);
|
|
|
|
break;
|
|
|
|
case PROP_PATH: // Path to the album
|
2021-05-07 21:52:42 +03:00
|
|
|
koto_indexed_album_update_path(self, (gchar*) g_value_get_string(val));
|
2021-03-23 19:50:09 +02:00
|
|
|
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;
|
|
|
|
case PROP_ARTIST_UUID:
|
|
|
|
koto_indexed_album_set_artist_uuid(self, g_value_get_string(val));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
|
|
|
break;
|
2021-03-02 19:12:12 +02:00
|
|
|
}
|
2021-03-23 19:50:09 +02:00
|
|
|
}
|
2021-03-02 19:12:12 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * koto_indexed_album_get_album_art(KotoIndexedAlbum * self) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return g_strdup("");
|
|
|
|
}
|
|
|
|
|
2021-05-07 21:52:42 +03:00
|
|
|
return g_strdup((self->has_album_art && koto_utils_is_string_valid(self->art_path)) ? self->art_path : "");
|
2021-05-07 16:45:57 +03:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * koto_indexed_album_get_album_name(KotoIndexedAlbum * self) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-05-07 21:52:42 +03:00
|
|
|
if (!koto_utils_is_string_valid(self->name)) { // Not set
|
2021-05-07 16:45:57 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_strdup(self->name); // Return duplicate of the name
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * koto_indexed_album_get_album_uuid(KotoIndexedAlbum * self) {
|
2021-05-07 21:52:42 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!koto_utils_is_string_valid(self->uuid)) { // Not set
|
2021-05-07 16:45:57 +03:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_strdup(self->uuid); // Return a duplicate of the UUID
|
2021-03-23 19:50:09 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
GList * koto_indexed_album_get_tracks(KotoIndexedAlbum * self) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
return self->tracks; // Return
|
2021-03-02 19:12:12 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_set_album_art(
|
|
|
|
KotoIndexedAlbum * self,
|
|
|
|
const gchar * album_art
|
|
|
|
) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_remove_file(
|
|
|
|
KotoIndexedAlbum * self,
|
|
|
|
KotoIndexedTrack * track
|
|
|
|
) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-16 08:51:35 +02:00
|
|
|
if (track == NULL) { // Not a file
|
2021-02-16 17:15:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * track_uuid;
|
|
|
|
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
g_object_get(track, "parsed-name", &track_uuid, NULL);
|
|
|
|
self->tracks = g_list_remove(self->tracks, track_uuid);
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_set_album_name(
|
|
|
|
KotoIndexedAlbum * self,
|
|
|
|
const gchar * album_name
|
|
|
|
) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-16 17:15:10 +02:00
|
|
|
if (album_name == NULL) { // Not valid album name
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->name != NULL) {
|
|
|
|
g_free(self->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
self->name = g_strdup(album_name);
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_set_artist_uuid(
|
|
|
|
KotoIndexedAlbum * self,
|
|
|
|
const gchar * artist_uuid
|
|
|
|
) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
if (artist_uuid == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->artist_uuid != NULL) {
|
|
|
|
g_free(self->artist_uuid);
|
|
|
|
}
|
|
|
|
|
|
|
|
self->artist_uuid = g_strdup(artist_uuid);
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_set_as_current_playlist(KotoIndexedAlbum * self) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-16 08:51:35 +02:00
|
|
|
if (self->tracks == NULL) { // No files to add to the playlist
|
2021-03-10 13:44:08 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
KotoPlaylist * new_album_playlist = koto_playlist_new(); // Create a new playlist
|
|
|
|
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
g_object_set(new_album_playlist, "ephemeral", TRUE, NULL); // Set as ephemeral / temporary
|
2021-03-10 13:44:08 +02:00
|
|
|
|
2021-05-07 16:45:57 +03:00
|
|
|
// The following section effectively reverses our tracks, so the first is now last.
|
|
|
|
// It then adds them in reverse order, since our playlist add function will prepend to our queue
|
|
|
|
// This enables the preservation and defaulting of "newest" first everywhere else, while in albums preserving the rightful order of the album
|
|
|
|
// e.g. first track (0) being added last is actually first in the playlist's tracks
|
2021-05-11 20:05:04 +03:00
|
|
|
GList * reversed_tracks = g_list_copy(self->tracks); // Copy our tracks so we can reverse the order
|
|
|
|
|
|
|
|
|
2021-05-07 16:45:57 +03:00
|
|
|
reversed_tracks = g_list_reverse(reversed_tracks); // Actually reverse it
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
GList * t;
|
|
|
|
|
|
|
|
|
2021-05-07 16:45:57 +03:00
|
|
|
for (t = reversed_tracks; t != NULL; t = t->next) { // For each of the tracks
|
|
|
|
gchar* track_uuid = t->data;
|
|
|
|
koto_playlist_add_track_by_uuid(new_album_playlist, track_uuid, FALSE, FALSE); // Add the UUID, skip commit to table since it is temporary
|
2021-03-10 13:44:08 +02:00
|
|
|
}
|
|
|
|
|
2021-05-07 16:45:57 +03:00
|
|
|
g_list_free(t);
|
|
|
|
g_list_free(reversed_tracks);
|
|
|
|
|
|
|
|
koto_playlist_apply_model(new_album_playlist, KOTO_PREFERRED_MODEL_TYPE_DEFAULT); // Ensure we are using our default model
|
2021-03-10 13:44:08 +02:00
|
|
|
koto_current_playlist_set_playlist(current_playlist, new_album_playlist); // Set our new current playlist
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
gint koto_indexed_album_sort_tracks(
|
|
|
|
gconstpointer track1_uuid,
|
|
|
|
gconstpointer track2_uuid,
|
|
|
|
gpointer user_data
|
|
|
|
) {
|
2021-03-23 19:50:09 +02:00
|
|
|
(void) user_data;
|
2021-05-11 20:05:04 +03:00
|
|
|
KotoIndexedTrack * track1 = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) track1_uuid);
|
|
|
|
KotoIndexedTrack * track2 = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) track2_uuid);
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
|
|
|
|
if ((track1 == NULL) && (track2 == NULL)) { // Neither tracks actually exist
|
|
|
|
return 0;
|
|
|
|
} else if ((track1 != NULL) && (track2 == NULL)) { // Only track2 does not exist
|
|
|
|
return -1;
|
|
|
|
} else if ((track1 == NULL) && (track2 != NULL)) { // Only track1 does not exist
|
|
|
|
return 1;
|
|
|
|
}
|
2021-02-16 17:15:10 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
guint * track1_disc = (guint*) 1;
|
|
|
|
guint * track2_disc = (guint*) 2;
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
|
|
|
|
g_object_get(track1, "cd", &track1_disc, NULL);
|
|
|
|
g_object_get(track2, "cd", &track2_disc, NULL);
|
|
|
|
|
|
|
|
if (track1_disc < track2_disc) { // Track 2 is in a later CD / Disc
|
|
|
|
return -1;
|
|
|
|
} else if (track1_disc > track2_disc) { // Track1 is later
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
guint16 * track1_pos;
|
|
|
|
guint16 * track2_pos;
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
|
|
|
|
g_object_get(track1, "position", &track1_pos, NULL);
|
|
|
|
g_object_get(track2, "position", &track2_pos, NULL);
|
|
|
|
|
|
|
|
if (track1_pos == track2_pos) { // Identical positions (like reported as 0)
|
2021-05-11 20:05:04 +03:00
|
|
|
gchar * track1_name;
|
|
|
|
gchar * track2_name;
|
2021-03-23 19:50:09 +02:00
|
|
|
|
|
|
|
g_object_get(track1, "parsed-name", &track1_name, NULL);
|
|
|
|
g_object_get(track2, "parsed-name", &track2_name, NULL);
|
|
|
|
|
|
|
|
return g_utf8_collate(track1_name, track2_name);
|
|
|
|
} else if (track1_pos < track2_pos) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
return 1;
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
void koto_indexed_album_update_path(
|
|
|
|
KotoIndexedAlbum * self,
|
|
|
|
gchar* new_path
|
|
|
|
) {
|
2021-05-07 16:45:57 +03:00
|
|
|
if (!KOTO_IS_INDEXED_ALBUM(self)) { // Not an album
|
2021-02-16 17:15:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-07 21:52:42 +03:00
|
|
|
if (!koto_utils_is_string_valid(new_path)) {
|
2021-05-07 16:45:57 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-07 21:52:42 +03:00
|
|
|
if (koto_utils_is_string_valid(self->path)) { // Path is currently set
|
2021-02-16 17:15:10 +02:00
|
|
|
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
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
if (!self->do_initial_index) { // Not doing our initial index
|
2021-02-16 17:15:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
koto_indexed_album_find_album_art(self); // Update our path for the album art
|
|
|
|
}
|
2021-02-16 17:15:10 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
KotoIndexedAlbum * koto_indexed_album_new(
|
|
|
|
KotoIndexedArtist * artist,
|
|
|
|
const gchar * path
|
|
|
|
) {
|
|
|
|
gchar * artist_uuid = NULL;
|
|
|
|
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
g_object_get(artist, "uuid", &artist_uuid, NULL);
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
KotoIndexedAlbum* album = g_object_new(
|
|
|
|
KOTO_TYPE_INDEXED_ALBUM,
|
|
|
|
"artist-uuid",
|
|
|
|
artist_uuid,
|
|
|
|
"uuid",
|
|
|
|
g_strdup(g_uuid_string_random()),
|
|
|
|
"do-initial-index",
|
|
|
|
TRUE,
|
|
|
|
"path",
|
|
|
|
path,
|
2021-03-02 19:12:12 +02:00
|
|
|
NULL
|
|
|
|
);
|
2021-02-16 17:15:10 +02:00
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
|
2021-03-02 19:12:12 +02:00
|
|
|
koto_indexed_album_commit(album);
|
|
|
|
koto_indexed_album_find_tracks(album, NULL, NULL); // Scan for tracks now that we committed to the database (hopefully)
|
|
|
|
|
|
|
|
return album;
|
2021-02-16 17:15:10 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
KotoIndexedAlbum * koto_indexed_album_new_with_uuid(
|
|
|
|
KotoIndexedArtist * artist,
|
|
|
|
const gchar * uuid
|
|
|
|
) {
|
|
|
|
gchar * artist_uuid = NULL;
|
|
|
|
|
|
|
|
|
2021-03-23 19:50:09 +02:00
|
|
|
g_object_get(artist, "uuid", &artist_uuid, NULL);
|
|
|
|
|
2021-05-11 20:05:04 +03:00
|
|
|
return g_object_new(
|
|
|
|
KOTO_TYPE_INDEXED_ALBUM,
|
|
|
|
"artist-uuid",
|
|
|
|
artist,
|
|
|
|
"uuid",
|
|
|
|
g_strdup(uuid),
|
|
|
|
"do-initial-index",
|
|
|
|
FALSE,
|
2021-02-16 17:15:10 +02:00
|
|
|
NULL
|
|
|
|
);
|
|
|
|
}
|