static struct song * apply_song_metadata(struct song *dest, const struct song *src) { struct song *tmp; assert(dest != NULL); assert(src != NULL); if (src->tag == NULL && src->start_ms == 0 && src->end_ms == 0) return dest; if (song_in_database(dest)) { char *path_fs = map_song_fs(dest); if (path_fs == NULL) return dest; tmp = song_file_new(path_fs, NULL); g_free(path_fs); merge_song_metadata(tmp, dest, src); } else { tmp = song_file_new(dest->uri, NULL); merge_song_metadata(tmp, dest, src); song_free(dest); } return tmp; }
bool song_file_update(struct song *song) { const char *suffix; char *path_fs; const struct decoder_plugin *plugin; struct stat st; assert(song_is_file(song)); /* check if there's a suffix and a plugin */ suffix = uri_get_suffix(song->url); if (suffix == NULL) return false; plugin = decoder_plugin_from_suffix(suffix, false); if (plugin == NULL) return false; path_fs = map_song_fs(song); if (path_fs == NULL) return false; if (song->tag != NULL) { tag_free(song->tag); song->tag = NULL; } if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) { g_free(path_fs); return false; } song->mtime = st.st_mtime; do { song->tag = plugin->tag_dup(path_fs); if (song->tag != NULL) break; plugin = decoder_plugin_from_suffix(suffix, true); } while (plugin != NULL); if (song->tag != NULL && tag_is_empty(song->tag)) song->tag = tag_fallback(path_fs, song->tag); g_free(path_fs); return song->tag != NULL; }
/* passed to songvec_for_each */ static int delete_song_if_removed(struct song *song, void *_data) { struct directory *dir = _data; char *path; struct stat st; if ((path = map_song_fs(song)) == NULL || stat(path, &st) < 0 || !S_ISREG(st.st_mode)) { delete_song(dir, song); modified = true; } g_free(path); return 0; }
void playlist_print_song(FILE *file, const struct song *song) { if (playlist_saveAbsolutePaths && song_in_database(song)) { char *path = map_song_fs(song); if (path != NULL) { fprintf(file, "%s\n", path); g_free(path); } } else { char *uri = song_get_uri(song), *uri_fs; uri_fs = utf8_to_fs_charset(uri); g_free(uri); fprintf(file, "%s\n", uri_fs); g_free(uri_fs); } }
static struct song * apply_song_metadata(struct song *dest, const struct song *src) { struct song *tmp; assert(dest != NULL); assert(src != NULL); if (src->tag == NULL && src->start_ms == 0 && src->end_ms == 0) return dest; if (song_in_database(dest)) { char *path_fs = map_song_fs(dest); if (path_fs == NULL) return dest; char *path_utf8 = fs_charset_to_utf8(path_fs); if (path_utf8 != NULL) g_free(path_fs); else path_utf8 = path_fs; tmp = song_file_new(path_utf8, NULL); g_free(path_utf8); merge_song_metadata(tmp, dest, src); } else { tmp = song_file_new(dest->uri, NULL); merge_song_metadata(tmp, dest, src); } if (dest->tag != NULL && dest->tag->time > 0 && src->start_ms > 0 && src->end_ms == 0 && src->start_ms / 1000 < (unsigned)dest->tag->time) /* the range is open-ended, and the playlist plugin did not know the total length of the song file (e.g. last track on a CUE file); fix it up here */ tmp->tag->time = dest->tag->time - src->start_ms / 1000; song_free(dest); return tmp; }
static void decoder_run(struct decoder_control *dc) { const struct song *song = dc->song; char *uri; assert(song != NULL); if (song_is_file(song)) uri = map_song_fs(song); else uri = song_get_uri(song); if (uri == NULL) { dc->state = DECODE_STATE_ERROR; decoder_command_finished_locked(dc); return; } decoder_run_song(dc, song, uri); g_free(uri); }
static void decoder_run_song(struct decoder_control *dc, const struct song *song, const char *uri) { struct decoder decoder = { .dc = dc, .initial_seek_pending = dc->start_ms > 0, .initial_seek_running = false, }; int ret; decoder.timestamp = 0.0; decoder.seeking = false; decoder.song_tag = song->tag != NULL && song_is_file(song) ? tag_dup(song->tag) : NULL; decoder.stream_tag = NULL; decoder.decoder_tag = NULL; decoder.chunk = NULL; dc->state = DECODE_STATE_START; decoder_command_finished_locked(dc); pcm_convert_init(&decoder.conv_state); ret = song_is_file(song) ? decoder_run_file(&decoder, uri) : decoder_run_stream(&decoder, uri); decoder_unlock(dc); pcm_convert_deinit(&decoder.conv_state); /* flush the last chunk */ if (decoder.chunk != NULL) decoder_flush_chunk(&decoder); if (decoder.song_tag != NULL) tag_free(decoder.song_tag); if (decoder.stream_tag != NULL) tag_free(decoder.stream_tag); if (decoder.decoder_tag != NULL) tag_free(decoder.decoder_tag); decoder_lock(dc); dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR; } static void decoder_run(struct decoder_control *dc) { const struct song *song = dc->song; char *uri; assert(song != NULL); if (song_is_file(song)) uri = map_song_fs(song); else uri = song_get_uri(song); if (uri == NULL) { dc->state = DECODE_STATE_ERROR; decoder_command_finished_locked(dc); return; } decoder_run_song(dc, song, uri); g_free(uri); } static gpointer decoder_task(gpointer arg) { struct decoder_control *dc = arg; decoder_lock(dc); do { assert(dc->state == DECODE_STATE_STOP || dc->state == DECODE_STATE_ERROR); switch (dc->command) { case DECODE_COMMAND_START: dc_mixramp_start(dc, NULL); dc_mixramp_prev_end(dc, dc->mixramp_end); dc->mixramp_end = NULL; /* Don't free, it's copied above. */ dc->replay_gain_prev_db = dc->replay_gain_db; dc->replay_gain_db = 0; /* fall through */ case DECODE_COMMAND_SEEK: decoder_run(dc); break; case DECODE_COMMAND_STOP: decoder_command_finished_locked(dc); break; case DECODE_COMMAND_NONE: decoder_wait(dc); break; } } while (dc->command != DECODE_COMMAND_NONE || !dc->quit); decoder_unlock(dc); return NULL; } void decoder_thread_start(struct decoder_control *dc) { GError *e = NULL; assert(dc->thread == NULL); dc->quit = false; dc->thread = g_thread_create(decoder_task, dc, true, &e); if (dc->thread == NULL) MPD_ERROR("Failed to spawn decoder task: %s", e->message); }
bool song_file_update(struct song *song) { const char *suffix; char *path_fs; const struct decoder_plugin *plugin; struct stat st; struct input_stream *is = NULL; assert(song_is_file(song)); /* check if there's a suffix and a plugin */ suffix = uri_get_suffix(song->uri); if (suffix == NULL) return false; plugin = decoder_plugin_from_suffix(suffix, NULL); if (plugin == NULL) return false; path_fs = map_song_fs(song); if (path_fs == NULL) return false; if (song->tag != NULL) { tag_free(song->tag); song->tag = NULL; } if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) { g_free(path_fs); return false; } song->mtime = st.st_mtime; do { /* load file tag */ song->tag = decoder_plugin_tag_dup(plugin, path_fs); if (song->tag != NULL) break; /* fall back to stream tag */ if (plugin->stream_tag != NULL) { /* open the input_stream (if not already open) */ if (is == NULL) is = input_stream_open(path_fs, NULL); /* now try the stream_tag() method */ if (is != NULL) { song->tag = decoder_plugin_stream_tag(plugin, is); if (song->tag != NULL) break; input_stream_seek(is, 0, SEEK_SET, NULL); } } plugin = decoder_plugin_from_suffix(suffix, plugin); } while (plugin != NULL); if (is != NULL) input_stream_close(is); if (song->tag != NULL && tag_is_empty(song->tag)) song->tag = tag_fallback(path_fs, song->tag); g_free(path_fs); return song->tag != NULL; }
bool song_file_update(struct song *song) { const char *suffix; char *path_fs; const struct decoder_plugin *plugin; struct stat st; struct input_stream *is = NULL; assert(song_is_file(song)); /* check if there's a suffix and a plugin */ suffix = uri_get_suffix(song->uri); if (suffix == NULL) return false; plugin = decoder_plugin_from_suffix(suffix, NULL); if (plugin == NULL) return false; path_fs = map_song_fs(song); if (path_fs == NULL) return false; if (song->tag != NULL) { tag_free(song->tag); song->tag = NULL; } if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) { g_free(path_fs); return false; } song->mtime = st.st_mtime; GMutex *mutex = NULL; GCond *cond; #if !GCC_CHECK_VERSION(4, 2) /* work around "may be used uninitialized in this function" false positive */ cond = NULL; #endif do { /* load file tag */ song->tag = tag_new(); if (decoder_plugin_scan_file(plugin, path_fs, &full_tag_handler, song->tag)) break; tag_free(song->tag); song->tag = NULL; /* fall back to stream tag */ if (plugin->scan_stream != NULL) { /* open the input_stream (if not already open) */ if (is == NULL) { mutex = g_mutex_new(); cond = g_cond_new(); is = input_stream_open(path_fs, mutex, cond, NULL); } /* now try the stream_tag() method */ if (is != NULL) { song->tag = tag_new(); if (decoder_plugin_scan_stream(plugin, is, &full_tag_handler, song->tag)) break; tag_free(song->tag); song->tag = NULL; input_stream_lock_seek(is, 0, SEEK_SET, NULL); } } plugin = decoder_plugin_from_suffix(suffix, plugin); } while (plugin != NULL); if (is != NULL) input_stream_close(is); if (mutex != NULL) { g_cond_free(cond); g_mutex_free(mutex); } if (song->tag != NULL && tag_is_empty(song->tag)) tag_scan_fallback(path_fs, &full_tag_handler, song->tag); g_free(path_fs); return song->tag != NULL; }
static void decoder_run_song(struct decoder_control *dc, const struct song *song, const char *uri) { struct decoder decoder = { .dc = dc, .initial_seek_pending = dc->start_ms > 0, .initial_seek_running = false, }; int ret; decoder.timestamp = 0.0; decoder.seeking = false; decoder.song_tag = song->tag != NULL && song_is_file(song) ? tag_dup(song->tag) : NULL; decoder.stream_tag = NULL; decoder.decoder_tag = NULL; decoder.chunk = NULL; dc->state = DECODE_STATE_START; decoder_command_finished_locked(dc); pcm_convert_init(&decoder.conv_state); ret = song_is_file(song) ? decoder_run_file(&decoder, uri) : decoder_run_stream(&decoder, uri); decoder_unlock(dc); pcm_convert_deinit(&decoder.conv_state); /* flush the last chunk */ if (decoder.chunk != NULL) decoder_flush_chunk(&decoder); if (decoder.song_tag != NULL) tag_free(decoder.song_tag); if (decoder.stream_tag != NULL) tag_free(decoder.stream_tag); if (decoder.decoder_tag != NULL) tag_free(decoder.decoder_tag); decoder_lock(dc); if (ret) dc->state = DECODE_STATE_STOP; else { dc->state = DECODE_STATE_ERROR; const char *error_uri = song->uri; char *allocated = uri_remove_auth(error_uri); if (allocated != NULL) error_uri = allocated; dc->error = g_error_new(decoder_quark(), 0, "Failed to decode %s", error_uri); g_free(allocated); } } static void decoder_run(struct decoder_control *dc) { dc_clear_error(dc); const struct song *song = dc->song; char *uri; assert(song != NULL); if (song_is_file(song)) uri = map_song_fs(song); else uri = song_get_uri(song); if (uri == NULL) { dc->state = DECODE_STATE_ERROR; dc->error = g_error_new(decoder_quark(), 0, "Failed to map song"); decoder_command_finished_locked(dc); return; } decoder_run_song(dc, song, uri); g_free(uri); }