/** \file playlist.c Functions for loading and saving playlists. */
/** Structure used for adding entries to a playlist. */
-struct playlist_info {
+struct playlist_instance {
/** The name of the playlist. */
char *name;
/** The number of entries currently in the playlist. */
unsigned length;
};
-static struct playlist_info current_playlist;
+static struct playlist_instance current_playlist;
/**
* Re-insert an audio file into the tree of admissible files.
static int add_playlist_entry(char *path, void *data)
{
- struct playlist_info *playlist = data;
+ struct playlist_instance *playlist = data;
struct osl_row *aft_row;
int ret = aft_get_row_of_path(path, &aft_row);
return 1;
}
-/* returns -E_PLAYLIST_LOADED on _success_ to terminate the loop */
-static int load_playlist(struct osl_row *row, void *data)
-{
- struct playlist_info *playlist = data;
- struct osl_object playlist_def;
- char *playlist_name;
- int ret;
-
- 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(FELF_READ_ONLY, playlist_def.data,
- playlist_def.size, add_playlist_entry, playlist);
- osl_close_disk_object(&playlist_def);
- if (ret < 0)
- goto err;
- ret = -E_PLAYLIST_EMPTY;
- if (!playlist->length)
- goto err;
- playlist->name = para_strdup(playlist_name);
- PARA_NOTICE_LOG("loaded playlist %s (%u files)\n", playlist->name,
- playlist->length);
- return -E_PLAYLIST_LOADED;
-err:
- if (ret != -E_DUMMY_ROW)
- PARA_NOTICE_LOG("unable to load playlist (%s)\n",
- para_strerror(-ret));
- return 1;
-}
-
static int check_playlist_path(char *path, void *data)
{
- struct para_buffer *pb = data;
+ struct afs_callback_arg *aca = data;
struct osl_row *aft_row;
int ret = aft_get_row_of_path(path, &aft_row);
if (ret < 0)
- para_printf(pb, "%s: %s\n", path, para_strerror(-ret));
+ afs_error(aca, "%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 afs_callback_arg *aca = data;
+ struct para_buffer *pb = &aca->pbout;
struct osl_object playlist_def;
char *playlist_name;
int ret = pl_get_name_and_def_by_row(row, &playlist_name, &playlist_def);
if (ret < 0) { /* log error, but continue */
- para_printf(pb, "failed to get playlist data: %s\n",
+ afs_error(aca, "failed to get playlist data: %s\n",
para_strerror(-ret));
return 1;
}
if (*playlist_name) { /* skip dummy row */
para_printf(pb, "checking playlist %s...\n", playlist_name);
for_each_line(FELF_READ_ONLY, playlist_def.data,
- playlist_def.size, check_playlist_path, pb);
+ playlist_def.size, check_playlist_path, aca);
}
osl_close_disk_object(&playlist_def);
return 1;
int playlist_check_callback(struct afs_callback_arg *aca)
{
para_printf(&aca->pbout, "checking playlists...\n");
- return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, &aca->pbout,
+ return osl(osl_rbtree_loop(playlists_table, BLOBCOL_ID, aca,
check_playlist));
}
-/**
- * Close the current playlist.
- *
- * \sa \ref playlist_open().
- */
-void playlist_close(void)
+/** Free all resources of the current playlist, if any. */
+void playlist_unload(void)
{
if (!current_playlist.name)
return;
free(current_playlist.name);
current_playlist.name = NULL;
+ current_playlist.length = 0;
}
/**
- * Open the given playlist.
+ * Populate the score table from the paths of a playlist database object.
*
- * \param name The name of the playlist to open.
+ * This loads the blob object which corresponds to the given name from the
+ * playlist table. Each line of the blob is regarded as a path which is looked
+ * up in the audio file table. If the path lookup succeeds, a reference to the
+ * corresponding row of the audio file table is added to the score table.
*
- * Files which are listed in the playlist, but not contained in the database
- * are ignored. This is not considered an error.
+ * \param name The name of the playlist to load.
+ * \param msg Error message or playlist info is returned here.
*
- * \return Standard.
+ * \return The length of the loaded playlist on success, negative error code
+ * else. Files which are listed in the playlist, but are not contained in the
+ * database are ignored. This is not considered an error.
*/
-int playlist_open(const char *name)
+int playlist_load(const char *name, char **msg)
{
- struct osl_object obj;
int ret;
- struct osl_row *row;
+ struct playlist_instance *playlist = ¤t_playlist;
+ struct osl_object playlist_def;
- obj.data = (char *)name;
- obj.size = strlen(obj.data);
- ret = osl(osl_get_row(playlists_table, BLOBCOL_NAME, &obj, &row));
+ ret = pl_get_def_by_name(name, &playlist_def);
if (ret < 0) {
- PARA_NOTICE_LOG("failed to load playlist %s\n", name);
+ *msg = make_message("could not read playlist %s\n", name);
return ret;
}
- playlist_close();
- ret = load_playlist(row, ¤t_playlist);
- return (ret == -E_PLAYLIST_LOADED)? current_playlist.length : ret;
+ playlist_unload();
+ 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;
+ ret = -E_PLAYLIST_EMPTY;
+ if (!playlist->length)
+ goto err;
+ playlist->name = para_strdup(name);
+ *msg = make_message("loaded playlist %s (%u files)\n", playlist->name,
+ playlist->length);
+ /* success */
+ return current_playlist.length;
+err:
+ PARA_NOTICE_LOG("unable to load playlist %s\n", name);
+ *msg = make_message("unable to load playlist %s\n", name);
+ return ret;
}
static int search_path(char *path, void *data)
static int handle_audio_file_event(enum afs_events event, void *data)
{
- int ret, was_admissible = 0, is_admissible;
+ int ret;
+ bool was_admissible = false, 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;
- }
+ if (event == AUDIO_FILE_RENAME)
+ was_admissible = row_belongs_to_score_table(row);
ret = get_audio_file_path_of_row(row, &new_path);
if (ret < 0)
return ret;
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)
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)
+ if (!row_belongs_to_score_table(data))
return 1;
current_playlist.length--;
return score_delete(data);