+/* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
+
+#include <regex.h>
+#include <osl.h>
+#include <lopsub.h>
+
#include "para.h"
#include "error.h"
+#include "string.h"
#include "afh.h"
#include "afs.h"
-#include "string.h"
+#include "ipc.h"
+#include "sideband.h"
/** \file playlist.c Functions for loading and saving playlists. */
*
* \return The return value of score_update().
*/
-int playlist_update_audio_file(struct osl_row *aft_row)
+static int playlist_update_audio_file(const struct osl_row *aft_row)
{
/* always re-insert to the top of the tree */
return score_update(aft_row, 0);
int ret = aft_get_row_of_path(path, &aft_row);
if (ret < 0) {
- PARA_NOTICE_LOG("%s: %s\n", path, PARA_STRERROR(-ret));
+ PARA_NOTICE_LOG("%s: %s\n", path, para_strerror(-ret));
return 1;
}
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++;
return 1;
}
-static int get_playlist_data(struct osl_row *row, char **playlist_name,
- struct osl_object *playlist_def)
-{
- struct osl_object obj;
- int ret = osl_get_object(playlists_table, row, BLOBCOL_NAME, &obj);
- if (ret < 0)
- return ret;
- *playlist_name = obj.data;
- return osl_open_disk_object(playlists_table, row, BLOBCOL_DEF,
- playlist_def);
-}
-
/* returns -E_PLAYLIST_LOADED on _success_ to terminate the loop */
static int load_playlist(struct osl_row *row, void *data)
{
char *playlist_name;
int ret;
- ret = get_playlist_data(row, &playlist_name, &playlist_def);
+ ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
if (ret < 0)
goto err;
playlist->length = 0;
- ret = for_each_line_ro(playlist_def.data, playlist_def.size,
- add_playlist_entry, playlist);
+ ret = for_each_line(FELF_READ_ONLY, playlist_def.data,
+ playlist_def.size, add_playlist_entry, playlist);
osl_close_disk_object(&playlist_def);
if (ret < 0)
goto err;
err:
if (ret != -E_DUMMY_ROW)
PARA_NOTICE_LOG("unable to load playlist (%s)\n",
- PARA_STRERROR(-ret));
+ para_strerror(-ret));
return 1;
}
-static int load_first_available_playlist(struct playlist_info *playlist)
-{
- int ret = osl_rbtree_loop(playlists_table, BLOBCOL_NAME, playlist,
- load_playlist);
- if (ret == -E_PLAYLIST_LOADED) /* success */
- return 1;
- if (ret < 0)
- return ret; /* error */
- PARA_NOTICE_LOG("no valid playlist found\n");
- return -E_NO_PLAYLIST;
-}
-
static int check_playlist_path(char *path, void *data)
{
struct para_buffer *pb = data;
int ret = aft_get_row_of_path(path, &aft_row);
if (ret < 0)
- para_printf(pb, "%s: %s\n", path, PARA_STRERROR(-ret));
- return 1;
+ para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+ return 1; /* do not fail the loop on bad paths */
}
static int check_playlist(struct osl_row *row, void *data)
struct para_buffer *pb = data;
struct osl_object playlist_def;
char *playlist_name;
- int ret = get_playlist_data(row, &playlist_name, &playlist_def);
+ int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
- if (ret < 0) {
+ if (ret < 0) { /* log error, but continue */
para_printf(pb, "failed to get playlist data: %s\n",
- PARA_STRERROR(-ret));
+ para_strerror(-ret));
return 1;
}
if (*playlist_name) { /* skip dummy row */
para_printf(pb, "checking playlist %s...\n", playlist_name);
- for_each_line_ro(playlist_def.data, playlist_def.size,
- check_playlist_path, pb);
+ for_each_line(FELF_READ_ONLY, playlist_def.data,
+ playlist_def.size, check_playlist_path, pb);
}
osl_close_disk_object(&playlist_def);
return 1;
}
-int playlist_check_callback(__a_unused const struct osl_object *query,
- struct osl_object *result)
+/**
+ * Check the playlist table for inconsistencies.
+ *
+ * \param aca This callback ignores ->query.
+ *
+ * \return Standard. Invalid paths are reported, but are not considered an
+ * error.
+ */
+int playlist_check_callback(struct afs_callback_arg *aca)
{
- struct para_buffer pb = {.buf = NULL};
-
- para_printf(&pb, "checking playlists...\n");
- osl_rbtree_loop(playlists_table, BLOBCOL_ID, &pb,
- check_playlist);
- result->data = pb.buf;
- result->size = pb.size;
- return 1;
+ para_printf(&aca->pbout, "checking playlists...\n");
+ return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, &aca->pbout,
+ check_playlist));
}
/**
* Close the current playlist.
*
- * \sa playlist_open().
+ * \sa \ref playlist_open().
*/
void playlist_close(void)
{
+ if (!current_playlist.name)
+ return;
free(current_playlist.name);
current_playlist.name = NULL;
}
*
* \param name The name of the playlist to open.
*
- * If name is \p NULL, load the first available playlist. Files which are
- * listed in the playlist, but not contained in the database are ignored.
- * This is not considered an error.
+ * Files which are listed in the playlist, but not contained in the database
+ * are ignored. This is not considered an error.
*
- * \return Positive on success, negative on errors. Possible errors
- * include: Given playlist not found, -E_NO_PLAYLIST (no playlist defined).
+ * \return Standard.
*/
-int playlist_open(char *name)
+int playlist_open(const char *name)
{
struct osl_object obj;
int ret;
struct osl_row *row;
- if (!name)
- return load_first_available_playlist(¤t_playlist);
- obj.data = name;
+ obj.data = (char *)name;
obj.size = strlen(obj.data);
- ret = osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row);
+ ret = osl(osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row));
if (ret < 0) {
PARA_NOTICE_LOG("failed to load playlist %s\n", name);
return ret;
}
- return load_playlist(row, ¤t_playlist);
+ playlist_close();
+ ret = load_playlist(row, ¤t_playlist);
+ return (ret == -E_PLAYLIST_LOADED)? current_playlist.length : 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 (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(FELF_READ_ONLY, 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 */
+}
+
+/**
+ * Handle afs events relevant to playlists.
+ *
+ * \param event The event type.
+ * \param pb Unused.
+ * \param data Depends on the event type.
+ *
+ * \return Standard.
+ */
+int playlists_event_handler(enum afs_events event,
+ __a_unused struct para_buffer *pb, void *data)
+{
+ int ret;
+ struct afsi_change_event_data *aced = data;
+
+ if (!current_playlist.name)
+ return 1;
+ switch (event) {
+ case AFSI_CHANGE:
+ return playlist_update_audio_file(aced->aft_row);
+ 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;
+ }
}