}
return 1;
}
+
+void afs_event(enum afs_events event, struct para_buffer *pb,
+ void *data)
+{
+ int i, ret;
+
+ for (i = 0; i < NUM_AFS_TABLES; i++) {
+ struct afs_table *t = &afs_tables[i];
+ if (!t->event_handler)
+ continue;
+ ret = t->event_handler(event, pb, data);
+ if (ret < 0)
+ PARA_CRIT_LOG("%s\n", PARA_STRERROR(-ret));
+ }
+}
+
+int images_event_handler(__a_unused enum afs_events event,
+ __a_unused struct para_buffer *pb, __a_unused void *data)
+{
+ return 1;
+}
+
+int lyrics_event_handler(__a_unused enum afs_events event,
+ __a_unused struct para_buffer *pb, __a_unused void *data)
+{
+ return 1;
+}
H: This adds new attributes to the attribute table. At most 64
H: attributes may be defined.
---
+N: mvatt
+P: AFS_READ | AFS_WRITE
+D: Rename an attribute.
+U: mvatt old new
+H: Rename attribute old to new.
+---
N: check
P: AFS_READ
D: Run integrity checks against osl tables.
};
enum afs_events {
+ ATTRIBUTE_ADD,
+ ATTRIBUTE_RENAME,
+ ATTRIBUTE_REMOVE,
AFSI_CHANGE,
AFHI_CHANGE,
AUDIO_FILE_RENAME,
AUDIO_FILE_ADD,
AUDIO_FILE_REMOVE,
- AUDIO_FILE_STREAMED,
- ATTRIBUTE_ADD,
- ATTRIBUTE_RENAME,
- ATTRIBUTE_DELETE,
- LYRICS_DELETE,
- IMAGE_DELETE,
+ LYRICS_ADD,
+ LYRICS_REMOVE,
LYRICS_RENAME,
+ IMAGE_ADD,
+ IMAGE_REMOVE,
IMAGE_RENAME,
};
+struct rmatt_event_data {
+ const char *name;
+ unsigned char bitnum;
+};
+
+
+struct addatt_event_data {
+ const char *name;
+ unsigned char bitnum;
+};
+
+struct afsi_change_event_data {
+ const struct osl_row *aft_row;
+ struct afs_info *old_afsi;
+};
+
+union afs_event_data {
+ struct {
+ const char *name;
+ unsigned char bitnum;
+ } rmatt_event_data;
+ struct osl_row *row;
+ struct {
+ const struct osl_row *row;
+ struct afs_info *old_afsi;
+ } afsi_change;
+
+};
+
struct afs_table {
void (*init)(struct afs_table *t);
const char *name;
int (*open)(const char *base_dir);
void (*close)(void);
int (*create)(const char *);
- int (*event_handler)(enum afs_events event, struct para_buffer *pb, void *data);
+ int (*event_handler)(enum afs_events event, struct para_buffer *pb,
+ void *data);
/* int *(check)() */
};
/* afs */
typedef int callback_function(const struct osl_object *, struct osl_object *);
__noreturn void afs_init(uint32_t cookie, int socket_fd);
+void afs_event(enum afs_events event, struct para_buffer *pb,
+ void *data);
int send_callback_request(callback_function *f, struct osl_object *query,
struct osl_object *result);
int send_standard_callback_request(int argc, char * const * const argv,
int score_update(const struct osl_row *aft_row, long new_score);
int get_num_admissible_files(unsigned *num);
int score_delete(const struct osl_row *aft_row);
-int row_belongs_to_score_table(const struct osl_row *aft_row);
+int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank);
/* attribute */
void attribute_init(struct afs_table *t);
/* mood */
int change_current_mood(char *mood_name);
-int mood_update_audio_file(const struct osl_row *aft_row, struct afs_info *old_afsi);
int reload_current_mood(void);
-int mood_delete_audio_file(const struct osl_row *aft_row);
int mood_check_callback(__a_unused const struct osl_object *query,
struct osl_object *result);
void table_name ## _init(struct afs_table *t); \
int cmd_prefix ## _get_name_by_id(uint32_t id, char **name); \
int cmd_prefix ## _get_def_by_id(uint32_t id, struct osl_object *def); \
+ int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def); \
int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \
char **name, struct osl_object *def); \
+ int table_name ##_event_handler(enum afs_events event, \
+ struct para_buffer *pb, void *data); \
extern struct osl_table *table_name ## _table;
DECLARE_BLOB_SYMBOLS(lyrics, lyr);
save_afsi(&new_afsi, &afsi_obj); /* in-place update */
if (afd->current_play_mode == PLAY_MODE_PLAYLIST)
ret = playlist_update_audio_file(aft_row);
- else
- ret = mood_update_audio_file(aft_row, &afd->afsi);
+ else {
+ struct afsi_change_event_data aced = {.aft_row = aft_row,
+ .old_afsi = &afd->afsi};
+ afs_event(AFSI_CHANGE, NULL, &aced);
+ }
return ret;
err:
free(afd->afhi.chunk_table);
sprintf(score_buf, "%li ", d->score);
}
- PARA_NOTICE_LOG("id: %s, %d\n", d->path, afsi->audio_format_id);
if (opts->mode == LS_MODE_LONG) {
para_printf(b,
"%s" /* score */
time_t current_time;
- PARA_NOTICE_LOG("%d patterns\n", opts->num_patterns);
if (opts->num_patterns) {
opts->patterns = para_malloc(opts->num_patterns * sizeof(char *));
for (i = 0, p = pattern_start; i < opts->num_patterns; i++) {
opts->patterns[i] = p;
p += strlen(p) + 1;
- PARA_NOTICE_LOG("pattern %d: %s\n", i, opts->patterns[i]);
}
} else
opts->patterns = NULL;
if (ret < 0)
goto out;
ret = opts->num_patterns? -E_NO_MATCH : 0;
- if (!opts->num_matching_paths) {
- PARA_NOTICE_LOG("no match, ret: %d\n", ret);
+ if (!opts->num_matching_paths)
goto out;
- }
ret = sort_matching_paths(opts);
if (ret < 0)
goto out;
ret = 1;
out:
ls_output->data = b.buf;
- PARA_NOTICE_LOG("ls_outoute.data: %p\n", ls_output->data);
ls_output->size = b.size;
free(opts->data);
free(opts->data_ptr);
{
char *buf = query->data, *path;
struct osl_row *pb, *aft_row;
- const struct osl_row *hs;
+ struct osl_row *hs;
struct osl_object objs[NUM_AFT_COLUMNS];
HASH_TYPE *hash;
char asc[2 * HASH_SIZE + 1];
pb = NULL;
}
/* file rename, update hs' path */
- ret = osl_get_object(audio_file_table, hs, AFTCOL_PATH, &obj);
- if (flags & ADD_FLAG_VERBOSE)
+ if (flags & ADD_FLAG_VERBOSE) {
+ ret = osl_get_object(audio_file_table, hs,
+ AFTCOL_PATH, &obj);
+ if (ret < 0)
+ goto out;
para_printf(&msg, "renamed from %s\n", (char *)obj.data);
+ }
ret = osl_update_object(audio_file_table, hs, AFTCOL_PATH,
&objs[AFTCOL_PATH]);
if (ret < 0)
goto out;
+ afs_event(AUDIO_FILE_RENAME, &msg, hs);
if (!(flags & ADD_FLAG_FORCE))
goto out;
}
goto out;
}
if (hs || pb) { /* (hs != NULL and pb != NULL) implies hs == pb */
- const struct osl_row *row = pb? pb : hs;
+ struct osl_row *row = pb? pb : hs;
/* update afhi and chunk_table */
if (flags & ADD_FLAG_VERBOSE)
para_printf(&msg, "updating afhi and chunk table\n");
goto out;
ret = osl_update_object(audio_file_table, row, AFTCOL_CHUNKS,
&objs[AFTCOL_CHUNKS]);
+ if (ret < 0)
+ goto out;
+ afs_event(AFHI_CHANGE, &msg, row);
goto out;
}
/* new entry, use default afsi */
struct afs_info old_afsi, new_afsi;
int ret, no_options = tad->cto->num_played < 0 && tad->cto->last_played < 0 &&
tad->cto->lyrics_id < 0 && tad->cto->image_id < 0;
+ struct afsi_change_event_data aced;
ret = get_afsi_object_of_row(row, &obj);
if (ret < 0) {
new_afsi.last_played = tad->cto->last_played;
}
save_afsi(&new_afsi, &obj); /* in-place update */
- ret = mood_update_audio_file(row, &old_afsi);
- if (ret < 0)
- para_printf(&tad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
+ aced.aft_row = row;
+ aced.old_afsi = &old_afsi;
+ afs_event(AFSI_CHANGE, &tad->pb, &aced);
return 1;
}
if (crd->flags & RM_FLAG_VERBOSE)
para_printf(&crd->pb, "removing %s\n", name);
- ret = mood_delete_audio_file(row);
- if (ret < 0)
- para_printf(&crd->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
+ afs_event(AUDIO_FILE_REMOVE, &crd->pb, row);
ret = osl_del_row(audio_file_table, row);
if (ret < 0)
para_printf(&crd->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
struct cpsi_action_data *cad = data;
struct osl_object target_afsi_obj;
int ret;
- struct afs_info target_afsi;
+ struct afs_info old_afsi, target_afsi;
+ struct afsi_change_event_data aced;
ret = get_afsi_object_of_row(row, &target_afsi_obj);
if (ret < 0)
return ret;
load_afsi(&target_afsi, &target_afsi_obj);
+ old_afsi = target_afsi;
if (cad->flags & CPSI_FLAG_COPY_LYRICS_ID)
target_afsi.lyrics_id = cad->source_afsi.lyrics_id;
if (cad->flags & CPSI_FLAG_COPY_IMAGE_ID)
cad->num_copied++;
if (cad->flags & CPSI_FLAG_VERBOSE)
para_printf(&cad->pb, "copied afsi to %s\n", name);
+ aced.aft_row = row;
+ aced.old_afsi = &old_afsi;
+ afs_event(AFSI_CHANGE, &cad->pb, &aced);
return 1;
}
return osl_create_table(&audio_file_table_desc);
}
+static int clear_attribute(struct osl_row *row, void *data)
+{
+ struct rmatt_event_data *red = data;
+ struct afs_info afsi;
+ struct osl_object obj;
+ int ret = get_afsi_object_of_row(row, &obj);
+ uint64_t mask = ~(1ULL << red->bitnum);
+
+ if (ret < 0)
+ return ret;
+ ret = load_afsi(&afsi, &obj);
+ if (ret < 0)
+ return ret;
+ afsi.attributes &= mask;
+ save_afsi(&afsi, &obj);
+ return 1;
+}
+
+static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
+ void *data)
+{
+ switch(event) {
+ case ATTRIBUTE_REMOVE: {
+ const struct rmatt_event_data *red = data;
+ para_printf(pb, "clearing attribute %s (bit %u) from all "
+ "entries in the audio file table\n", red->name,
+ red->bitnum);
+ return audio_file_loop(data, clear_attribute);
+ }
+ default:
+ return 1;
+ }
+}
+
void aft_init(struct afs_table *t)
{
t->name = audio_file_table_desc.name;
t->open = aft_open;
t->close = aft_close;
t->create = aft_create;
+ t->event_handler = aft_event_handler;
}
NULL);
}
-/* TODO: make it faster by only extracting the attribute member from afsi */
-static int logical_and_attribute(struct osl_row *aft_row, void *attribute_ptr)
-{
- struct afs_info afsi;
- uint64_t *att = attribute_ptr;
- struct osl_object obj;
- int ret = get_afsi_object_of_row(aft_row, &obj);
- if (ret < 0)
- return ret;
- ret = load_afsi(&afsi, &obj);
- if (ret < 0)
- return ret;
- afsi.attributes &= *att;
- save_afsi(&afsi, &obj);
- return 1;
-}
-
static int com_addatt_callback(const struct osl_object *query,
struct osl_object *result)
{
struct osl_row *row;
unsigned char bitnum;
len = strlen(p);
+ struct addatt_event_data aed;
if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
para_printf(&pb, "invalid attribute name: %s\n", p);
objs[ATTCOL_NAME].data = p;
objs[ATTCOL_NAME].size = len + 1;
ret = osl_get_row(attribute_table, ATTCOL_NAME,
- &objs[ATTCOL_NAME], &row); /* expected to fail */
+ &objs[ATTCOL_NAME], &row); /* expected to fail FIXME: Use get_attribute_bitnum_by_name() */
if (ret >= 0) {
para_printf(&pb, "attribute %s already exists\n", p);
continue;
}
if (ret != -E_RB_KEY_NOT_FOUND) /* error */
goto out;
- /* find smallest non-used attribute */
+ /* find smallest non-used attribute FIXME: Use find_greatest_att_bitnum() */
for (bitnum = 0; bitnum < 64; bitnum++) {
objs[ATTCOL_BITNUM].data = &bitnum;
ret = osl_get_row(attribute_table, ATTCOL_BITNUM,
ret = osl_add_row(attribute_table, objs);
if (ret < 0)
goto out;
+ aed.name = p;
+ aed.bitnum = bitnum;
+ afs_event(ATTRIBUTE_ADD, &pb, &aed);
greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, bitnum);
}
out:
return ret;
}
+static int com_mvatt_callback(const struct osl_object *query,
+ struct osl_object *result)
+{
+ char *old = query->data;
+ size_t size = strlen(old) + 1;
+ char *new = old + size;
+ struct osl_object obj = {.data = old, .size = size};
+ struct osl_row *row;
+ struct para_buffer pb = {.size = 0};
+ int ret;
+
+ ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row);
+ if (ret < 0)
+ goto out;
+ obj.data = new;
+ obj.size = strlen(new) + 1;
+ ret = osl_update_object(attribute_table, row, ATTCOL_NAME, &obj);
+out:
+ if (ret < 0)
+ para_printf(&pb, "%s\n", PARA_STRERROR(-ret));
+ else
+ afs_event(ATTRIBUTE_RENAME, &pb, NULL);
+ if (!pb.buf)
+ return 0;
+ result->data = pb.buf;
+ result->size = pb.size;
+ return 1;
+}
+
+int com_mvatt(int fd, int argc, char * const * const argv)
+{
+ struct osl_object result;
+ int ret;
+
+ if (argc != 3)
+ return -E_ATTR_SYNTAX;
+ ret = send_standard_callback_request(argc - 1, argv + 1, com_mvatt_callback,
+ &result);
+ if (!ret)
+ return 1;
+ if (ret < 0)
+ return ret;
+ if (!result.data || !result.size)
+ return 1;
+ ret = send_va_buffer(fd, "%s", (char *) result.data);
+ free(result.data);
+ return ret;
+}
+
+
struct remove_attribute_action_data {
struct para_buffer pb;
int num_removed;
const char *name, void *data)
{
struct remove_attribute_action_data *raad = data;
- unsigned char bitnum;
int ret;
+ struct rmatt_event_data red = {.name = name};
- ret = get_attribute_bitnum_by_name(name, &bitnum);
+ ret = get_attribute_bitnum_by_name(name, &red.bitnum);
if (ret < 0) {
para_printf(&raad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
return 1;
para_printf(&raad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
return 1;
}
- para_printf(&raad->pb, "removed %s\n", name);
+ para_printf(&raad->pb, "removed attribute %s\n", name);
raad->num_removed++;
- raad->mask_of_removed_atts |= (1 << bitnum);
+ raad->mask_of_removed_atts |= (1 << red.bitnum);
+ afs_event(ATTRIBUTE_REMOVE, &raad->pb, &red);
return 1;
}
ret = for_each_matching_row(&pmd);
if (ret < 0)
para_printf(&raad.pb, "%s\n", PARA_STRERROR(-ret));
- if (raad.num_removed) {
- uint64_t and_mask = ~raad.mask_of_removed_atts;
- ret = audio_file_loop(&and_mask, logical_and_attribute);
- if (ret < 0)
- para_printf(&raad.pb, "%s\n", PARA_STRERROR(-ret));
- find_greatest_att_bitnum();
- ret = reload_current_mood();
- if (ret < 0)
- para_printf(&raad.pb, "%s\n", PARA_STRERROR(-ret));
- }
- if (!raad.pb.buf)
+ if (!raad.num_removed)
para_printf(&raad.pb, "no match -- nothing removed\n");
result->data = raad.pb.buf;
result->size = raad.pb.size;
}
/**
- * Return a binary representation of the geiven attribute value.
+ * Return a binary representation of the given attribute value.
*
* \param atts Pointer to the attribute value.
* \param buf Result.
return blob_get_name_by_id(table_name ## _table, id, name); \
}
+
+static int blob_get_def_by_name(struct osl_table *table, char *name,
+ struct osl_object *def)
+{
+ struct osl_row *row;
+ struct osl_object obj = {.data = name, .size = strlen(name) + 1};
+ int ret;
+
+ def->data = NULL;
+ if (!*name)
+ return 1;
+ ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
+ if (ret < 0)
+ return ret;
+ return osl_open_disk_object(table, row, BLOBCOL_DEF, def);
+}
+
+/** Define the \p get_def_by_id function for this blob type. */
+#define DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix) \
+ int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def) \
+ { \
+ return blob_get_def_by_name(table_name ## _table, name, def); \
+ }
+
static int blob_get_def_by_id(struct osl_table *table, uint32_t id,
struct osl_object *def)
{
t->open = table_name ## _open; \
t->close = table_name ## _close; \
t->create = table_name ## _create;\
+ t->event_handler = table_name ##_event_handler; \
}
DEFINE_BLOB_COMMAND(mv, table_name, cmd_prefix) \
DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix); \
DEFINE_GET_DEF_BY_ID(table_name, cmd_prefix); \
+ DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix); \
DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix); \
/** \cond doxygen isn't smart enough to recognize these */
#define PLAYLIST_ERRORS \
PARA_ERROR(NO_PLAYLIST, "no valid playlist found"), \
PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
+ PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
*
* \return Positive on success, negative on errors.
*
- * \sa score_delete(), mood_update_audio_file().
+ * \sa score_delete().
*/
-int mood_delete_audio_file(const struct osl_row *aft_row)
+static int mood_delete_audio_file(const struct osl_row *aft_row)
{
int ret;
- ret = row_belongs_to_score_table(aft_row);
+ ret = row_belongs_to_score_table(aft_row, NULL);
if (ret < 0)
return ret;
if (!ret) /* not admissible, nothing to do */
*
* \return Positive on success, negative on errors.
*/
-int mood_update_audio_file(const struct osl_row *aft_row, struct afs_info *old_afsi)
+static int mood_update_audio_file(const struct osl_row *aft_row,
+ struct afs_info *old_afsi)
{
long score, percent;
int ret, is_admissible, was_admissible = 0;
struct afs_info afsi;
+ unsigned rank;
if (!current_mood)
return 1; /* nothing to do */
- ret = row_belongs_to_score_table(aft_row);
+ ret = row_belongs_to_score_table(aft_row, &rank);
if (ret < 0)
return ret;
was_admissible = ret;
percent = 100;
else if (percent < 0)
percent = 0;
- PARA_DEBUG_LOG("re-inserting at %lu%%\n", percent);
+ PARA_DEBUG_LOG("moving from rank %u to %lu%%\n", rank, percent);
return score_update(aft_row, percent);
}
if (!current_mood)
return 1;
-// score_close(0);
mood_name = para_strdup(current_mood->name);
close_current_mood();
ret = change_current_mood(mood_name);
free(mood_name);
return ret;
}
+
+int moods_event_handler(enum afs_events event, struct para_buffer *pb,
+ void *data)
+{
+ switch(event) {
+ case ATTRIBUTE_ADD:
+ case ATTRIBUTE_REMOVE:
+ case ATTRIBUTE_RENAME:
+ return reload_current_mood();
+ case AFSI_CHANGE: {
+ struct afsi_change_event_data *aced = data;
+ return mood_update_audio_file(aced->aft_row, aced->old_afsi);
+ }
+ case AFHI_CHANGE:
+ case AUDIO_FILE_RENAME:
+ case AUDIO_FILE_ADD:
+ return mood_update_audio_file(data, NULL);
+ case AUDIO_FILE_REMOVE:
+ return mood_delete_audio_file(data);
+ default:
+ return 1;
+ }
+ return 1;
+}
+
if (ret < 0)
goto err;
ret = -E_BAD_DB_DIR;
- if (!t->desc->dir)
+ if (!t->desc->dir && (t->num_disk_storage_columns || t->num_mapped_columns))
goto err;
/* the size of the index header without column descriptions */
t->index_header_size = IDX_COLUMN_DESCRIPTIONS;
*
* \param desc Pointer to the table description.
*
- * \return Positive on success, negative on errors. Possible errors include: \p
- * E_BAD_TABLE_DESC, \p E_BAD_DB_DIR, \p E_BAD_NAME, \p E_NO_COMPARE_FUNC, \p
- * E_NO_COLUMN_NAME, \p E_DUPLICATE_COL_NAME, \p E_MKDIR, any errors returned
- * by para_open().
+ * \return Standard.
*/
int osl_create_table(const struct osl_table_description *desc)
{
* The table description given by \a desc should coincide with the
* description used at creation time.
*
- * \return Positive on success, negative on errors. Possible errors include:
- * errors returned by init_table_structure(), \p E_NOENT, \p E_STAT, \p \p
- * E_NOTDIR, \p E_BAD_TABLE_DESC, \p E_BAD_DB_DIR, \p E_NO_COMPARE_FUNC, \p
- * E_NO_COLUMN_NAME, errors returned by init_rbtrees().
+ * \return Standard.
*/
int osl_open_table(const struct osl_table_description *table_desc,
struct osl_table **result)
* mapped columns of constant size (which may be updated directly if \p
* OSL_RBTREE is not set). Otherwise the rbtree might become corrupted.
*
- * \return Positive on success, negative on errors. Possible errors include: \p
- * E_BAD_TABLE, \p E_RB_KEY_EXISTS, \p E_BAD_SIZE, \p E_NOENT, \p E_UNLINK,
- * errors returned by para_write_file(), \p E_MKDIR.
+ * \return Standard
*/
int osl_update_object(struct osl_table *t, const struct osl_row *r,
unsigned col_num, struct osl_object *obj)
* \param rank Result pointer.
*
* The rank is, by definition, the position of the row in the linear order
- * determined by an inorder tree walk of the rbtree associated with column
+ * determined by an in-order tree walk of the rbtree associated with column
* number \a col_num of \a table.
*
* \return Positive on success, negative on errors.
}
ret = score_add(aft_row, -playlist->length);
if (ret < 0) {
- PARA_ERROR_LOG("failed to add %s: %d\n", path, ret);
+ PARA_ERROR_LOG("failed to add %s: %s\n", path, PARA_STRERROR(-ret));
return ret;
}
playlist->length++;
PARA_NOTICE_LOG("failed to load playlist %s\n", name);
return ret;
}
- return load_playlist(row, ¤t_playlist);
+ ret = load_playlist(row, ¤t_playlist);
+ return (ret == -E_PLAYLIST_LOADED)? 1 : ret;
+}
+
+static int search_path(char *path, void *data)
+{
+ if (strcmp(path, data))
+ return 1;
+ return -E_PATH_FOUND;
+}
+
+static int handle_audio_file_event(enum afs_events event, void *data)
+{
+ int ret, was_admissible = 0, is_admissible;
+ struct osl_object playlist_def;
+ char *new_path;
+ const struct osl_row *row = data;
+
+ if (!current_playlist.name)
+ return 1;
+ if (event == AUDIO_FILE_RENAME) {
+ ret = row_belongs_to_score_table(row, NULL);
+ if (ret < 0)
+ return ret;
+ was_admissible = ret;
+ }
+ ret = get_audio_file_path_of_row(row, &new_path);
+ if (ret < 0)
+ return ret;
+ ret = pl_get_def_by_name(current_playlist.name, &playlist_def);
+ if (ret < 0)
+ return ret;
+ ret = for_each_line_ro(playlist_def.data, playlist_def.size,
+ search_path, new_path);
+ osl_close_disk_object(&playlist_def);
+ is_admissible = (ret < 0);
+ if (was_admissible && is_admissible)
+ return 1;
+ if (!was_admissible && !is_admissible)
+ return 1;
+ if (was_admissible && !is_admissible) {
+ current_playlist.length--;
+ return score_delete(row);
+ }
+ /* !was_admissible && is_admissible */
+ current_playlist.length++;
+ return score_add(row, 0); /* play it immediately */
+}
+
+int playlists_event_handler(enum afs_events event, struct para_buffer *pb,
+ void *data)
+{
+ int ret;
+
+ switch(event) {
+ case AUDIO_FILE_RENAME:
+ case AUDIO_FILE_ADD:
+ return handle_audio_file_event(event, data);
+ case AUDIO_FILE_REMOVE:
+ ret = row_belongs_to_score_table(data, NULL);
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return 1;
+ current_playlist.length--;
+ return score_delete(data);
+ default:
+ return 1;
+ }
}
* \param obj2 Pointer to the second score object.
*
* This function first compares the score values as usual integers. If they compare as
- * equal, the addresss of \a obj1 and \a obj2 are compared. So this compare function
+ * equal, the address of \a obj1 and \a obj2 are compared. So this compare function
* returns zero if and only if \a obj1 and \a obj2 point to the same memory area.
*
* \sa osl_compare_function.
}
/**
- * The score table consists of two colums: The \a aft_row column contains
+ * The score table consists of two columns: The \a aft_row column contains
* pointers to the rows of the audio file table, and the score column contains
* the current score of the audio file associated with that row.
*/
// PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data));
ret = osl_add_row(score_table, score_objs);
if (ret < 0) {
- PARA_ERROR_LOG("failed to add row: %d\n", ret);
+ PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
free(score_objs[SCORECOL_AFT_ROW].data);
free(score_objs[SCORECOL_SCORE].data);
}
* \param aft_row Determines the audio file to change.
* \param percent The position to re-insert the audio file.
*
- * The percent paramenter must be between \p 0 and 100 and. A value of zero
+ * The percent parameter must be between \p 0 and 100 and. A value of zero
* means to re-insert the audio file into the score table with a score lower
* than any other admissible file.
*
obj.size = sizeof(long);
obj.data = para_malloc(obj.size);
*(long *)obj.data = new_score;
- PARA_DEBUG_LOG("new score: %ld, position: %u/%u\n", new_score,
- new_pos, n);
+ PARA_DEBUG_LOG("new score: %ld, rank %u/%u\n", new_score, new_pos, n);
return osl_update_object(score_table, row, SCORECOL_SCORE, &obj);
}
* Find out whether an audio file is contained in the score table.
*
* \param aft_row The row of the audio file table.
+ * \param rank Result pointer
*
* \return Positive, if \a aft_row belongs to the audio file table,
- * zero if not, negative on errors.
+ * zero if not, negative on errors. If \a aft_row was found, and \a rank
+ * is not \p NULL, the rank of \a aft_row is returned in \a rank.
*/
-int row_belongs_to_score_table(const struct osl_row *aft_row)
+int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank)
{
struct osl_row *score_row;
int ret = get_score_row_from_aft_row(aft_row, &score_row);
- if (ret >= 0)
- return 1;
+
if (ret == -E_RB_KEY_NOT_FOUND)
return 0;
- return ret;
+ if (ret < 0)
+ return ret;
+ if (!rank)
+ return 1;
+ ret = osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank);
+ if (ret < 0)
+ return ret;
+ return 1;
}
/* Close the score table. */
/**
* Open the score table.
*
- * \param dir The database directory.
+ * \param dir Unused.
*
* \return The return value of the underlying call to osl_open_table().
- *
- * \sa score_shutdown().
*/
-static int score_open(const char *dir)
+static int score_open(__a_unused const char *dir)
{
- score_table_desc.dir = dir;
+ score_table_desc.dir = NULL; /* this table has only volatile columns */
return osl_open_table(&score_table_desc, &score_table);
}
+static int score_event_handler(enum afs_events event, struct para_buffer *pb,
+ void *data)
+{
+ int ret;
+
+ switch(event) {
+ case ATTRIBUTE_ADD:
+ case ATTRIBUTE_REMOVE:
+ case ATTRIBUTE_RENAME: {
+ score_close();
+ return score_open(NULL);
+ }
+ default: return 1;
+ }
+}
+
/**
* Initialize the scoring subsystem.
*
- * \param t Members Gets filled in by the function.
+ * \param t The members of \t are filled in by the function.
*/
void score_init(struct afs_table *t)
{
t->name = score_table_desc.name;
t->open = score_open;
t->close = score_close;
+ t->event_handler = score_event_handler;
}