1 /* Copyright (C) 2007 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
15 /** \file playlist.c Functions for loading and saving playlists. */
17 /** Structure used for adding entries to a playlist. */
18 struct playlist_instance
{
19 /** The name of the playlist. */
21 /** The number of entries currently in the playlist. */
24 static struct playlist_instance current_playlist
;
27 * Re-insert an audio file into the tree of admissible files.
29 * \param aft_row Determines the audio file.
31 * \return The return value of score_update().
33 static int playlist_update_audio_file(const struct osl_row
*aft_row
)
35 /* always re-insert to the top of the tree */
36 return score_update(aft_row
, 0);
39 static int add_playlist_entry(char *path
, void *data
)
41 struct playlist_instance
*playlist
= data
;
42 struct osl_row
*aft_row
;
43 int ret
= aft_get_row_of_path(path
, &aft_row
);
46 PARA_NOTICE_LOG("%s: %s\n", path
, para_strerror(-ret
));
49 ret
= score_add(aft_row
, -playlist
->length
);
51 PARA_ERROR_LOG("failed to add %s: %s\n", path
, para_strerror(-ret
));
58 static int check_playlist_path(char *path
, void *data
)
60 struct afs_callback_arg
*aca
= data
;
61 struct osl_row
*aft_row
;
62 int ret
= aft_get_row_of_path(path
, &aft_row
);
65 afs_error(aca
, "%s: %s\n", path
, para_strerror(-ret
));
66 return 1; /* do not fail the loop on bad paths */
69 static int check_playlist(struct osl_row
*row
, void *data
)
71 struct afs_callback_arg
*aca
= data
;
72 struct para_buffer
*pb
= &aca
->pbout
;
73 struct osl_object playlist_def
;
75 int ret
= pl_get_name_and_def_by_row(row
, &playlist_name
, &playlist_def
);
77 if (ret
< 0) { /* log error, but continue */
78 afs_error(aca
, "failed to get playlist data: %s\n",
82 if (*playlist_name
) { /* skip dummy row */
83 para_printf(pb
, "checking playlist %s...\n", playlist_name
);
84 for_each_line(FELF_READ_ONLY
, playlist_def
.data
,
85 playlist_def
.size
, check_playlist_path
, aca
);
87 osl_close_disk_object(&playlist_def
);
92 * Check the playlist table for inconsistencies.
94 * \param aca This callback ignores ->query.
96 * \return Standard. Invalid paths are reported, but are not considered an
99 int playlist_check_callback(struct afs_callback_arg
*aca
)
101 para_printf(&aca
->pbout
, "checking playlists...\n");
102 return osl(osl_rbtree_loop(playlists_table
, BLOBCOL_ID
, aca
,
106 /** Free all resources of the current playlist, if any. */
107 void playlist_unload(void)
109 if (!current_playlist
.name
)
111 free(current_playlist
.name
);
112 current_playlist
.name
= NULL
;
113 current_playlist
.length
= 0;
117 * Populate the score table from the paths of a playlist database object.
119 * This loads the blob object which corresponds to the given name from the
120 * playlist table. Each line of the blob is regarded as a path which is looked
121 * up in the audio file table. If the path lookup succeeds, a reference to the
122 * corresponding row of the audio file table is added to the score table.
124 * \param name The name of the playlist to load.
125 * \param msg Error message or playlist info is returned here.
127 * \return The length of the loaded playlist on success, negative error code
128 * else. Files which are listed in the playlist, but are not contained in the
129 * database are ignored. This is not considered an error.
131 int playlist_load(const char *name
, char **msg
)
134 struct playlist_instance
*playlist
= ¤t_playlist
;
135 struct osl_object playlist_def
;
137 ret
= pl_get_def_by_name(name
, &playlist_def
);
139 *msg
= make_message("could not read playlist %s\n", name
);
143 ret
= for_each_line(FELF_READ_ONLY
, playlist_def
.data
,
144 playlist_def
.size
, add_playlist_entry
, playlist
);
145 osl_close_disk_object(&playlist_def
);
148 ret
= -E_PLAYLIST_EMPTY
;
149 if (!playlist
->length
)
151 playlist
->name
= para_strdup(name
);
152 *msg
= make_message("loaded playlist %s (%u files)\n", playlist
->name
,
155 return current_playlist
.length
;
157 PARA_NOTICE_LOG("unable to load playlist %s\n", name
);
158 *msg
= make_message("unable to load playlist %s\n", name
);
162 static int search_path(char *path
, void *data
)
164 if (strcmp(path
, data
))
166 return -E_PATH_FOUND
;
169 static int handle_audio_file_event(enum afs_events event
, void *data
)
172 bool was_admissible
= false, is_admissible
;
173 struct osl_object playlist_def
;
175 const struct osl_row
*row
= data
;
177 if (event
== AUDIO_FILE_RENAME
)
178 was_admissible
= row_belongs_to_score_table(row
);
179 ret
= get_audio_file_path_of_row(row
, &new_path
);
182 ret
= pl_get_def_by_name(current_playlist
.name
, &playlist_def
);
185 ret
= for_each_line(FELF_READ_ONLY
, playlist_def
.data
,
186 playlist_def
.size
, search_path
, new_path
);
187 osl_close_disk_object(&playlist_def
);
188 is_admissible
= (ret
< 0);
189 if (was_admissible
&& is_admissible
)
191 if (!was_admissible
&& !is_admissible
)
193 if (was_admissible
&& !is_admissible
) {
194 current_playlist
.length
--;
195 return score_delete(row
);
197 /* !was_admissible && is_admissible */
198 current_playlist
.length
++;
199 return score_add(row
, 0); /* play it immediately */
203 * Handle afs events relevant to playlists.
205 * \param event The event type.
207 * \param data Depends on the event type.
211 int playlists_event_handler(enum afs_events event
,
212 __a_unused
struct para_buffer
*pb
, void *data
)
214 struct afsi_change_event_data
*aced
= data
;
216 if (!current_playlist
.name
)
220 return playlist_update_audio_file(aced
->aft_row
);
221 case AUDIO_FILE_RENAME
:
223 return handle_audio_file_event(event
, data
);
224 case AUDIO_FILE_REMOVE
:
225 if (!row_belongs_to_score_table(data
))
227 current_playlist
.length
--;
228 return score_delete(data
);