decoder_plugin: scan tags with callback table
Pass a callback table to scan_file() and scan_stream(), instead of returning a tag object.
This commit is contained in:
parent
b7356bc526
commit
5d73215a8d
@ -335,6 +335,7 @@ src_mpd_SOURCES = \
|
|||||||
src/tag_pool.c \
|
src/tag_pool.c \
|
||||||
src/tag_print.c \
|
src/tag_print.c \
|
||||||
src/tag_save.c \
|
src/tag_save.c \
|
||||||
|
src/tag_handler.c src/tag_handler.h \
|
||||||
src/tokenizer.c \
|
src/tokenizer.c \
|
||||||
src/text_file.c \
|
src/text_file.c \
|
||||||
src/text_input_stream.c \
|
src/text_input_stream.c \
|
||||||
@ -1006,6 +1007,7 @@ test_dump_playlist_SOURCES = test/dump_playlist.c \
|
|||||||
src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
|
src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
|
||||||
src/uri.c \
|
src/uri.c \
|
||||||
src/song.c src/tag.c src/tag_pool.c src/tag_save.c \
|
src/song.c src/tag.c src/tag_pool.c src/tag_save.c \
|
||||||
|
src/tag_handler.c \
|
||||||
src/text_input_stream.c src/fifo_buffer.c \
|
src/text_input_stream.c src/fifo_buffer.c \
|
||||||
src/cue/cue_parser.c src/cue/cue_parser.h \
|
src/cue/cue_parser.c src/cue/cue_parser.h \
|
||||||
src/fd_util.c
|
src/fd_util.c
|
||||||
@ -1027,7 +1029,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \
|
|||||||
test/stdbin.h \
|
test/stdbin.h \
|
||||||
src/io_thread.c src/io_thread.h \
|
src/io_thread.c src/io_thread.h \
|
||||||
src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
|
src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
|
||||||
src/tag.c src/tag_pool.c \
|
src/tag.c src/tag_pool.c src/tag_handler.c \
|
||||||
src/replay_gain_info.c \
|
src/replay_gain_info.c \
|
||||||
src/uri.c \
|
src/uri.c \
|
||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
@ -1049,7 +1051,7 @@ test_read_tags_LDADD = \
|
|||||||
test_read_tags_SOURCES = test/read_tags.c \
|
test_read_tags_SOURCES = test/read_tags.c \
|
||||||
src/io_thread.c src/io_thread.h \
|
src/io_thread.c src/io_thread.h \
|
||||||
src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
|
src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
|
||||||
src/tag.c src/tag_pool.c \
|
src/tag.c src/tag_pool.c src/tag_handler.c \
|
||||||
src/replay_gain_info.c \
|
src/replay_gain_info.c \
|
||||||
src/uri.c \
|
src/uri.c \
|
||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#include <audiofile.h>
|
#include <audiofile.h>
|
||||||
#include <af_vfs.h>
|
#include <af_vfs.h>
|
||||||
@ -222,20 +223,20 @@ audiofile_stream_decode(struct decoder *decoder, struct input_stream *is)
|
|||||||
afCloseFile(af_fp);
|
afCloseFile(af_fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *audiofile_tag_dup(const char *file)
|
static bool
|
||||||
|
audiofile_scan_file(const char *file,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
struct tag *ret = NULL;
|
|
||||||
int total_time = audiofile_get_duration(file);
|
int total_time = audiofile_get_duration(file);
|
||||||
|
|
||||||
if (total_time >= 0) {
|
if (total_time < 0) {
|
||||||
ret = tag_new();
|
|
||||||
ret->time = total_time;
|
|
||||||
} else {
|
|
||||||
g_debug("Failed to get total song time from: %s\n",
|
g_debug("Failed to get total song time from: %s\n",
|
||||||
file);
|
file);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
tag_handler_invoke_duration(handler, handler_ctx, total_time);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const audiofile_suffixes[] = {
|
static const char *const audiofile_suffixes[] = {
|
||||||
@ -251,7 +252,7 @@ static const char *const audiofile_mime_types[] = {
|
|||||||
const struct decoder_plugin audiofile_decoder_plugin = {
|
const struct decoder_plugin audiofile_decoder_plugin = {
|
||||||
.name = "audiofile",
|
.name = "audiofile",
|
||||||
.stream_decode = audiofile_stream_decode,
|
.stream_decode = audiofile_stream_decode,
|
||||||
.tag_dup = audiofile_tag_dup,
|
.scan_file = audiofile_scan_file,
|
||||||
.suffixes = audiofile_suffixes,
|
.suffixes = audiofile_suffixes,
|
||||||
.mime_types = audiofile_mime_types,
|
.mime_types = audiofile_mime_types,
|
||||||
};
|
};
|
||||||
|
@ -433,8 +433,10 @@ dsdiff_stream_decode(struct decoder *decoder, struct input_stream *is)
|
|||||||
dsd2pcm_destroy(dsd2pcm[i]);
|
dsd2pcm_destroy(dsd2pcm[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
dsdiff_stream_tag(struct input_stream *is)
|
dsdiff_scan_stream(struct input_stream *is,
|
||||||
|
G_GNUC_UNUSED const struct tag_handler *handler,
|
||||||
|
G_GNUC_UNUSED void *handler_ctx)
|
||||||
{
|
{
|
||||||
struct dsdiff_metadata metadata = {
|
struct dsdiff_metadata metadata = {
|
||||||
.sample_rate = 0,
|
.sample_rate = 0,
|
||||||
@ -443,17 +445,17 @@ dsdiff_stream_tag(struct input_stream *is)
|
|||||||
|
|
||||||
struct dsdiff_chunk_header chunk_header;
|
struct dsdiff_chunk_header chunk_header;
|
||||||
if (!dsdiff_read_metadata(NULL, is, &metadata, &chunk_header))
|
if (!dsdiff_read_metadata(NULL, is, &metadata, &chunk_header))
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
struct audio_format audio_format;
|
struct audio_format audio_format;
|
||||||
if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8,
|
if (!audio_format_init_checked(&audio_format, metadata.sample_rate / 8,
|
||||||
SAMPLE_FORMAT_S24_P32,
|
SAMPLE_FORMAT_S24_P32,
|
||||||
metadata.channels, NULL))
|
metadata.channels, NULL))
|
||||||
/* refuse to parse files which we cannot play anyway */
|
/* refuse to parse files which we cannot play anyway */
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
/* no total time estimate, no tags implemented yet */
|
/* no total time estimate, no tags implemented yet */
|
||||||
return tag_new();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const dsdiff_suffixes[] = {
|
static const char *const dsdiff_suffixes[] = {
|
||||||
@ -470,7 +472,7 @@ const struct decoder_plugin dsdiff_decoder_plugin = {
|
|||||||
.name = "dsdiff",
|
.name = "dsdiff",
|
||||||
.init = dsdiff_init,
|
.init = dsdiff_init,
|
||||||
.stream_decode = dsdiff_stream_decode,
|
.stream_decode = dsdiff_stream_decode,
|
||||||
.stream_tag = dsdiff_stream_tag,
|
.scan_stream = dsdiff_scan_stream,
|
||||||
.suffixes = dsdiff_suffixes,
|
.suffixes = dsdiff_suffixes,
|
||||||
.mime_types = dsdiff_mime_types,
|
.mime_types = dsdiff_mime_types,
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
#include "decoder_buffer.h"
|
#include "decoder_buffer.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#define AAC_MAX_CHANNELS 6
|
#define AAC_MAX_CHANNELS 6
|
||||||
|
|
||||||
@ -487,18 +488,17 @@ faad_stream_decode(struct decoder *mpd_decoder, struct input_stream *is)
|
|||||||
faacDecClose(decoder);
|
faacDecClose(decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
faad_stream_tag(struct input_stream *is)
|
faad_scan_stream(struct input_stream *is,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
int file_time = faad_get_file_time(is);
|
int file_time = faad_get_file_time(is);
|
||||||
struct tag *tag;
|
|
||||||
|
|
||||||
if (file_time < 0)
|
if (file_time < 0)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
tag = tag_new();
|
tag_handler_invoke_duration(handler, handler_ctx, file_time);
|
||||||
tag->time = file_time;
|
return true;
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const faad_suffixes[] = { "aac", NULL };
|
static const char *const faad_suffixes[] = { "aac", NULL };
|
||||||
@ -509,7 +509,7 @@ static const char *const faad_mime_types[] = {
|
|||||||
const struct decoder_plugin faad_decoder_plugin = {
|
const struct decoder_plugin faad_decoder_plugin = {
|
||||||
.name = "faad",
|
.name = "faad",
|
||||||
.stream_decode = faad_stream_decode,
|
.stream_decode = faad_stream_decode,
|
||||||
.stream_tag = faad_stream_tag,
|
.scan_stream = faad_scan_stream,
|
||||||
.suffixes = faad_suffixes,
|
.suffixes = faad_suffixes,
|
||||||
.mime_types = faad_mime_types,
|
.mime_types = faad_mime_types,
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
#include "ffmpeg_metadata.h"
|
#include "ffmpeg_metadata.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
@ -570,22 +571,23 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//no tag reading in ffmpeg, check if playable
|
//no tag reading in ffmpeg, check if playable
|
||||||
static struct tag *
|
static bool
|
||||||
ffmpeg_stream_tag(struct input_stream *is)
|
ffmpeg_scan_stream(struct input_stream *is,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
AVInputFormat *input_format = ffmpeg_probe(NULL, is);
|
AVInputFormat *input_format = ffmpeg_probe(NULL, is);
|
||||||
if (input_format == NULL)
|
if (input_format == NULL)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
struct mpd_ffmpeg_stream *stream = mpd_ffmpeg_stream_open(NULL, is);
|
struct mpd_ffmpeg_stream *stream = mpd_ffmpeg_stream_open(NULL, is);
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
AVFormatContext *f = NULL;
|
AVFormatContext *f = NULL;
|
||||||
if (mpd_ffmpeg_open_input(&f, stream->io, is->uri,
|
if (mpd_ffmpeg_open_input(&f, stream->io, is->uri,
|
||||||
input_format) != 0) {
|
input_format) != 0) {
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0)
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0)
|
||||||
@ -601,23 +603,22 @@ ffmpeg_stream_tag(struct input_stream *is)
|
|||||||
av_close_input_stream(f);
|
av_close_input_stream(f);
|
||||||
#endif
|
#endif
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tag *tag = tag_new();
|
if (f->duration != (int64_t)AV_NOPTS_VALUE)
|
||||||
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
tag->time = f->duration != (int64_t)AV_NOPTS_VALUE
|
f->duration / AV_TIME_BASE);
|
||||||
? f->duration / AV_TIME_BASE
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,101,0)
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,101,0)
|
||||||
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
|
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ffmpeg_copy_dictionary(tag, f->metadata);
|
ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx);
|
||||||
int idx = ffmpeg_find_audio_stream(f);
|
int idx = ffmpeg_find_audio_stream(f);
|
||||||
if (idx >= 0)
|
if (idx >= 0)
|
||||||
ffmpeg_copy_dictionary(tag, f->streams[idx]->metadata);
|
ffmpeg_scan_dictionary(f->streams[idx]->metadata,
|
||||||
|
handler, handler_ctx);
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
avformat_close_input(&f);
|
avformat_close_input(&f);
|
||||||
@ -626,7 +627,7 @@ ffmpeg_stream_tag(struct input_stream *is)
|
|||||||
#endif
|
#endif
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
|
|
||||||
return tag;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -745,7 +746,7 @@ const struct decoder_plugin ffmpeg_decoder_plugin = {
|
|||||||
.name = "ffmpeg",
|
.name = "ffmpeg",
|
||||||
.init = ffmpeg_init,
|
.init = ffmpeg_init,
|
||||||
.stream_decode = ffmpeg_decode,
|
.stream_decode = ffmpeg_decode,
|
||||||
.stream_tag = ffmpeg_stream_tag,
|
.scan_stream = ffmpeg_scan_stream,
|
||||||
.suffixes = ffmpeg_suffixes,
|
.suffixes = ffmpeg_suffixes,
|
||||||
.mime_types = ffmpeg_mime_types
|
.mime_types = ffmpeg_mime_types
|
||||||
};
|
};
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "ffmpeg_metadata.h"
|
#include "ffmpeg_metadata.h"
|
||||||
#include "tag.h"
|
|
||||||
#include "tag_table.h"
|
#include "tag_table.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "ffmpeg"
|
#define G_LOG_DOMAIN "ffmpeg"
|
||||||
@ -39,23 +39,27 @@ static const struct tag_table ffmpeg_tags[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ffmpeg_copy_metadata(struct tag *tag, enum tag_type type,
|
ffmpeg_copy_metadata(enum tag_type type,
|
||||||
AVDictionary *m, const char *name)
|
AVDictionary *m, const char *name,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
AVDictionaryEntry *mt = NULL;
|
AVDictionaryEntry *mt = NULL;
|
||||||
|
|
||||||
while ((mt = av_dict_get(m, name, mt, 0)) != NULL)
|
while ((mt = av_dict_get(m, name, mt, 0)) != NULL)
|
||||||
tag_add_item(tag, type, mt->value);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
type, mt->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ffmpeg_copy_dictionary(struct tag *tag, AVDictionary *dict)
|
ffmpeg_scan_dictionary(AVDictionary *dict,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
||||||
ffmpeg_copy_metadata(tag, i,
|
ffmpeg_copy_metadata(i, dict, tag_item_names[i],
|
||||||
dict, tag_item_names[i]);
|
handler, handler_ctx);
|
||||||
|
|
||||||
for (const struct tag_table *i = ffmpeg_tags;
|
for (const struct tag_table *i = ffmpeg_tags;
|
||||||
i->name != NULL; ++i)
|
i->name != NULL; ++i)
|
||||||
ffmpeg_copy_metadata(tag, i->type, dict, i->name);
|
ffmpeg_copy_metadata(i->type, dict, i->name,
|
||||||
|
handler, handler_ctx);
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,10 @@
|
|||||||
#define av_dict_get av_metadata_get
|
#define av_dict_get av_metadata_get
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct tag;
|
struct tag_handler;
|
||||||
|
|
||||||
void
|
void
|
||||||
ffmpeg_copy_dictionary(struct tag *tag, AVDictionary *dict);
|
ffmpeg_scan_dictionary(AVDictionary *dict,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -191,10 +191,11 @@ flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame,
|
|||||||
return flac_common_write(data, frame, buf, nbytes);
|
return flac_common_write(data, frame, buf, nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
flac_tag_dup(const char *file)
|
flac_scan_file(const char *file,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
return flac_tag_load(file, NULL);
|
return flac_scan_file2(file, NULL, handler, handler_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -400,38 +401,33 @@ oggflac_init(G_GNUC_UNUSED const struct config_param *param)
|
|||||||
|
|
||||||
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
|
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
oggflac_tag_dup(const char *file)
|
oggflac_scan_file(const char *file,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
struct tag *ret = NULL;
|
|
||||||
FLAC__Metadata_Iterator *it;
|
FLAC__Metadata_Iterator *it;
|
||||||
FLAC__StreamMetadata *block;
|
FLAC__StreamMetadata *block;
|
||||||
FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
|
FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
|
||||||
|
|
||||||
if (!(FLAC__metadata_chain_read_ogg(chain, file))) {
|
if (!(FLAC__metadata_chain_read_ogg(chain, file))) {
|
||||||
FLAC__metadata_chain_delete(chain);
|
FLAC__metadata_chain_delete(chain);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
it = FLAC__metadata_iterator_new();
|
it = FLAC__metadata_iterator_new();
|
||||||
FLAC__metadata_iterator_init(it, chain);
|
FLAC__metadata_iterator_init(it, chain);
|
||||||
|
|
||||||
ret = tag_new();
|
|
||||||
do {
|
do {
|
||||||
if (!(block = FLAC__metadata_iterator_get_block(it)))
|
if (!(block = FLAC__metadata_iterator_get_block(it)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
flac_tag_apply_metadata(ret, NULL, block);
|
flac_scan_metadata(NULL, block,
|
||||||
|
handler, handler_ctx);
|
||||||
} while (FLAC__metadata_iterator_next(it));
|
} while (FLAC__metadata_iterator_next(it));
|
||||||
FLAC__metadata_iterator_delete(it);
|
FLAC__metadata_iterator_delete(it);
|
||||||
|
|
||||||
if (!tag_is_defined(ret)) {
|
|
||||||
tag_free(ret);
|
|
||||||
ret = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FLAC__metadata_chain_delete(chain);
|
FLAC__metadata_chain_delete(chain);
|
||||||
return ret;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -464,7 +460,7 @@ const struct decoder_plugin oggflac_decoder_plugin = {
|
|||||||
.init = oggflac_init,
|
.init = oggflac_init,
|
||||||
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
|
#if defined(FLAC_API_VERSION_CURRENT) && FLAC_API_VERSION_CURRENT > 7
|
||||||
.stream_decode = oggflac_decode,
|
.stream_decode = oggflac_decode,
|
||||||
.tag_dup = oggflac_tag_dup,
|
.scan_file = oggflac_scan_file,
|
||||||
.suffixes = oggflac_suffixes,
|
.suffixes = oggflac_suffixes,
|
||||||
.mime_types = oggflac_mime_types
|
.mime_types = oggflac_mime_types
|
||||||
#endif
|
#endif
|
||||||
@ -484,7 +480,7 @@ static const char *const flac_mime_types[] = {
|
|||||||
const struct decoder_plugin flac_decoder_plugin = {
|
const struct decoder_plugin flac_decoder_plugin = {
|
||||||
.name = "flac",
|
.name = "flac",
|
||||||
.stream_decode = flac_decode,
|
.stream_decode = flac_decode,
|
||||||
.tag_dup = flac_tag_dup,
|
.scan_file = flac_scan_file,
|
||||||
.suffixes = flac_suffixes,
|
.suffixes = flac_suffixes,
|
||||||
.mime_types = flac_mime_types,
|
.mime_types = flac_mime_types,
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "flac_metadata.h"
|
#include "flac_metadata.h"
|
||||||
#include "replay_gain_info.h"
|
#include "replay_gain_info.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
#include "tag_table.h"
|
#include "tag_table.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
@ -164,17 +165,19 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
|||||||
* the comment value into the tag.
|
* the comment value into the tag.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
flac_copy_comment(struct tag *tag,
|
flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
||||||
const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
|
||||||
const char *name, enum tag_type tag_type,
|
const char *name, enum tag_type tag_type,
|
||||||
const char *char_tnum)
|
const char *char_tnum,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
const char *value;
|
const char *value;
|
||||||
size_t value_length;
|
size_t value_length;
|
||||||
|
|
||||||
value = flac_comment_value(entry, name, char_tnum, &value_length);
|
value = flac_comment_value(entry, name, char_tnum, &value_length);
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
tag_add_item_n(tag, tag_type, value, value_length);
|
char *p = g_strndup(value, value_length);
|
||||||
|
tag_handler_invoke_tag(handler, handler_ctx, tag_type, p);
|
||||||
|
g_free(p);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,42 +192,47 @@ static const struct tag_table flac_tags[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
flac_parse_comment(struct tag *tag, const char *char_tnum,
|
flac_scan_comment(const char *char_tnum,
|
||||||
const FLAC__StreamMetadata_VorbisComment_Entry *entry)
|
const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
assert(tag != NULL);
|
|
||||||
|
|
||||||
for (const struct tag_table *i = flac_tags; i->name != NULL; ++i)
|
for (const struct tag_table *i = flac_tags; i->name != NULL; ++i)
|
||||||
if (flac_copy_comment(tag, entry, i->name, i->type, char_tnum))
|
if (flac_copy_comment(entry, i->name, i->type, char_tnum,
|
||||||
|
handler, handler_ctx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
||||||
if (flac_copy_comment(tag, entry,
|
if (flac_copy_comment(entry,
|
||||||
tag_item_names[i], i, char_tnum))
|
tag_item_names[i], i, char_tnum,
|
||||||
|
handler, handler_ctx))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
|
flac_scan_comments(const char *char_tnum,
|
||||||
const FLAC__StreamMetadata_VorbisComment *comment)
|
const FLAC__StreamMetadata_VorbisComment *comment,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < comment->num_comments; ++i)
|
for (unsigned i = 0; i < comment->num_comments; ++i)
|
||||||
flac_parse_comment(tag, char_tnum, &comment->comments[i]);
|
flac_scan_comment(char_tnum, &comment->comments[i],
|
||||||
|
handler, handler_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
flac_tag_apply_metadata(struct tag *tag, const char *track,
|
flac_scan_metadata(const char *track,
|
||||||
const FLAC__StreamMetadata *block)
|
const FLAC__StreamMetadata *block,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
switch (block->type) {
|
switch (block->type) {
|
||||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||||
flac_vorbis_comments_to_tag(tag, track,
|
flac_scan_comments(track, &block->data.vorbis_comment,
|
||||||
&block->data.vorbis_comment);
|
handler, handler_ctx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||||
if (block->data.stream_info.sample_rate > 0)
|
if (block->data.stream_info.sample_rate > 0)
|
||||||
tag->time = flac_duration(&block->data.stream_info);
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
|
flac_duration(&block->data.stream_info));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -232,10 +240,18 @@ flac_tag_apply_metadata(struct tag *tag, const char *track,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tag *
|
void
|
||||||
flac_tag_load(const char *file, const char *char_tnum)
|
flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
|
||||||
|
const FLAC__StreamMetadata_VorbisComment *comment)
|
||||||
|
{
|
||||||
|
flac_scan_comments(char_tnum, comment,
|
||||||
|
&add_tag_handler, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
flac_scan_file2(const char *file, const char *char_tnum,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
struct tag *tag;
|
|
||||||
FLAC__Metadata_SimpleIterator *it;
|
FLAC__Metadata_SimpleIterator *it;
|
||||||
FLAC__StreamMetadata *block = NULL;
|
FLAC__StreamMetadata *block = NULL;
|
||||||
|
|
||||||
@ -262,22 +278,30 @@ flac_tag_load(const char *file, const char *char_tnum)
|
|||||||
g_debug("Reading '%s' metadata gave the following error: %s\n",
|
g_debug("Reading '%s' metadata gave the following error: %s\n",
|
||||||
file, err);
|
file, err);
|
||||||
FLAC__metadata_simple_iterator_delete(it);
|
FLAC__metadata_simple_iterator_delete(it);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = tag_new();
|
|
||||||
do {
|
do {
|
||||||
block = FLAC__metadata_simple_iterator_get_block(it);
|
block = FLAC__metadata_simple_iterator_get_block(it);
|
||||||
if (!block)
|
if (!block)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
flac_tag_apply_metadata(tag, char_tnum, block);
|
flac_scan_metadata(char_tnum, block, handler, handler_ctx);
|
||||||
FLAC__metadata_object_delete(block);
|
FLAC__metadata_object_delete(block);
|
||||||
} while (FLAC__metadata_simple_iterator_next(it));
|
} while (FLAC__metadata_simple_iterator_next(it));
|
||||||
|
|
||||||
FLAC__metadata_simple_iterator_delete(it);
|
FLAC__metadata_simple_iterator_delete(it);
|
||||||
|
|
||||||
if (!tag_is_defined(tag)) {
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tag *
|
||||||
|
flac_tag_load(const char *file, const char *char_tnum)
|
||||||
|
{
|
||||||
|
struct tag *tag = tag_new();
|
||||||
|
|
||||||
|
if (!flac_scan_file2(file, char_tnum, &add_tag_handler, tag) ||
|
||||||
|
tag_is_empty(tag)) {
|
||||||
tag_free(tag);
|
tag_free(tag);
|
||||||
tag = NULL;
|
tag = NULL;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <FLAC/metadata.h>
|
#include <FLAC/metadata.h>
|
||||||
|
|
||||||
|
struct tag_handler;
|
||||||
struct tag;
|
struct tag;
|
||||||
struct replay_gain_info;
|
struct replay_gain_info;
|
||||||
|
|
||||||
@ -49,8 +50,13 @@ flac_vorbis_comments_to_tag(struct tag *tag, const char *char_tnum,
|
|||||||
const FLAC__StreamMetadata_VorbisComment *comment);
|
const FLAC__StreamMetadata_VorbisComment *comment);
|
||||||
|
|
||||||
void
|
void
|
||||||
flac_tag_apply_metadata(struct tag *tag, const char *track,
|
flac_scan_metadata(const char *track,
|
||||||
const FLAC__StreamMetadata *block);
|
const FLAC__StreamMetadata *block,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx);
|
||||||
|
|
||||||
|
bool
|
||||||
|
flac_scan_file2(const char *file, const char *char_tnum,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx);
|
||||||
|
|
||||||
struct tag *
|
struct tag *
|
||||||
flac_tag_load(const char *file, const char *char_tnum);
|
flac_tag_load(const char *file, const char *char_tnum);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "../decoder_api.h"
|
#include "../decoder_api.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
#include "uri.h"
|
#include "uri.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -180,8 +181,9 @@ gme_file_decode(struct decoder *decoder, const char *path_fs)
|
|||||||
gme_delete(emu);
|
gme_delete(emu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
gme_tag_dup(const char *path_fs)
|
gme_scan_file(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
Music_Emu *emu;
|
Music_Emu *emu;
|
||||||
gme_info_t *ti;
|
gme_info_t *ti;
|
||||||
@ -194,42 +196,49 @@ gme_tag_dup(const char *path_fs)
|
|||||||
g_free(path_container);
|
g_free(path_container);
|
||||||
if (gme_err != NULL) {
|
if (gme_err != NULL) {
|
||||||
g_warning("%s", gme_err);
|
g_warning("%s", gme_err);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
if((gme_err = gme_track_info(emu, &ti, song_num)) != NULL){
|
if((gme_err = gme_track_info(emu, &ti, song_num)) != NULL){
|
||||||
g_warning("%s", gme_err);
|
g_warning("%s", gme_err);
|
||||||
gme_delete(emu);
|
gme_delete(emu);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ti != NULL);
|
assert(ti != NULL);
|
||||||
|
|
||||||
struct tag *tag = tag_new();
|
|
||||||
|
|
||||||
if(ti->length > 0)
|
if(ti->length > 0)
|
||||||
tag->time = ti->length / 1000;
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
|
ti->length / 100);
|
||||||
|
|
||||||
if(ti->song != NULL){
|
if(ti->song != NULL){
|
||||||
if(gme_track_count(emu) > 1){
|
if(gme_track_count(emu) > 1){
|
||||||
/* start numbering subtunes from 1 */
|
/* start numbering subtunes from 1 */
|
||||||
char *tag_title=g_strdup_printf("%s (%d/%d)",
|
char *tag_title=g_strdup_printf("%s (%d/%d)",
|
||||||
ti->song, song_num+1, gme_track_count(emu));
|
ti->song, song_num+1, gme_track_count(emu));
|
||||||
tag_add_item(tag, TAG_TITLE, tag_title);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_TITLE, tag_title);
|
||||||
g_free(tag_title);
|
g_free(tag_title);
|
||||||
}else
|
}else
|
||||||
tag_add_item(tag, TAG_TITLE, ti->song);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_TITLE, ti->song);
|
||||||
}
|
}
|
||||||
if(ti->author != NULL)
|
if(ti->author != NULL)
|
||||||
tag_add_item(tag, TAG_ARTIST, ti->author);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_ARTIST, ti->author);
|
||||||
if(ti->game != NULL)
|
if(ti->game != NULL)
|
||||||
tag_add_item(tag, TAG_ALBUM, ti->game);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_ALBUM, ti->game);
|
||||||
if(ti->comment != NULL)
|
if(ti->comment != NULL)
|
||||||
tag_add_item(tag, TAG_COMMENT, ti->comment);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_COMMENT, ti->comment);
|
||||||
if(ti->copyright != NULL)
|
if(ti->copyright != NULL)
|
||||||
tag_add_item(tag, TAG_DATE, ti->copyright);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_DATE, ti->copyright);
|
||||||
|
|
||||||
gme_free_info(ti);
|
gme_free_info(ti);
|
||||||
gme_delete(emu);
|
gme_delete(emu);
|
||||||
return tag;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const gme_suffixes[] = {
|
static const char *const gme_suffixes[] = {
|
||||||
@ -242,7 +251,7 @@ extern const struct decoder_plugin gme_decoder_plugin;
|
|||||||
const struct decoder_plugin gme_decoder_plugin = {
|
const struct decoder_plugin gme_decoder_plugin = {
|
||||||
.name = "gme",
|
.name = "gme",
|
||||||
.file_decode = gme_file_decode,
|
.file_decode = gme_file_decode,
|
||||||
.tag_dup = gme_tag_dup,
|
.scan_file = gme_scan_file,
|
||||||
.suffixes = gme_suffixes,
|
.suffixes = gme_suffixes,
|
||||||
.container_scan = gme_container_scan,
|
.container_scan = gme_container_scan,
|
||||||
};
|
};
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "tag_id3.h"
|
#include "tag_id3.h"
|
||||||
#include "tag_rva2.h"
|
#include "tag_rva2.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -1176,19 +1177,18 @@ mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
|
|||||||
mp3_data_finish(&data);
|
mp3_data_finish(&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
mad_decoder_stream_tag(struct input_stream *is)
|
mad_decoder_scan_stream(struct input_stream *is,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
struct tag *tag;
|
|
||||||
int total_time;
|
int total_time;
|
||||||
|
|
||||||
total_time = mad_decoder_total_file_time(is);
|
total_time = mad_decoder_total_file_time(is);
|
||||||
if (total_time < 0)
|
if (total_time < 0)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
tag = tag_new();
|
tag_handler_invoke_duration(handler, handler_ctx, total_time);
|
||||||
tag->time = total_time;
|
return true;
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const mp3_suffixes[] = { "mp3", "mp2", NULL };
|
static const char *const mp3_suffixes[] = { "mp3", "mp2", NULL };
|
||||||
@ -1198,7 +1198,7 @@ const struct decoder_plugin mad_decoder_plugin = {
|
|||||||
.name = "mad",
|
.name = "mad",
|
||||||
.init = mp3_plugin_init,
|
.init = mp3_plugin_init,
|
||||||
.stream_decode = mp3_decode,
|
.stream_decode = mp3_decode,
|
||||||
.stream_tag = mad_decoder_stream_tag,
|
.scan_stream = mad_decoder_scan_stream,
|
||||||
.suffixes = mp3_suffixes,
|
.suffixes = mp3_suffixes,
|
||||||
.mime_types = mp3_mime_types
|
.mime_types = mp3_mime_types
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <modplug.h>
|
#include <modplug.h>
|
||||||
@ -149,34 +150,33 @@ mod_decode(struct decoder *decoder, struct input_stream *is)
|
|||||||
ModPlug_Unload(f);
|
ModPlug_Unload(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
modplug_stream_tag(struct input_stream *is)
|
modplug_scan_stream(struct input_stream *is,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
ModPlugFile *f;
|
ModPlugFile *f;
|
||||||
struct tag *ret = NULL;
|
|
||||||
GByteArray *bdatas;
|
GByteArray *bdatas;
|
||||||
char *title;
|
|
||||||
|
|
||||||
bdatas = mod_loadfile(NULL, is);
|
bdatas = mod_loadfile(NULL, is);
|
||||||
if (!bdatas)
|
if (!bdatas)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
f = ModPlug_Load(bdatas->data, bdatas->len);
|
f = ModPlug_Load(bdatas->data, bdatas->len);
|
||||||
g_byte_array_free(bdatas, TRUE);
|
g_byte_array_free(bdatas, TRUE);
|
||||||
if (f == NULL)
|
if (f == NULL)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
ret = tag_new();
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
ret->time = ModPlug_GetLength(f) / 1000;
|
ModPlug_GetLength(f) / 1000);
|
||||||
|
|
||||||
title = g_strdup(ModPlug_GetName(f));
|
const char *title = ModPlug_GetName(f);
|
||||||
if (title)
|
if (title != NULL)
|
||||||
tag_add_item(ret, TAG_TITLE, title);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
g_free(title);
|
TAG_TITLE, title);
|
||||||
|
|
||||||
ModPlug_Unload(f);
|
ModPlug_Unload(f);
|
||||||
|
|
||||||
return ret;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const mod_suffixes[] = {
|
static const char *const mod_suffixes[] = {
|
||||||
@ -189,6 +189,6 @@ static const char *const mod_suffixes[] = {
|
|||||||
const struct decoder_plugin modplug_decoder_plugin = {
|
const struct decoder_plugin modplug_decoder_plugin = {
|
||||||
.name = "modplug",
|
.name = "modplug",
|
||||||
.stream_decode = mod_decode,
|
.stream_decode = mod_decode,
|
||||||
.stream_tag = modplug_stream_tag,
|
.scan_stream = modplug_scan_stream,
|
||||||
.suffixes = mod_suffixes,
|
.suffixes = mod_suffixes,
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#ifdef MPC_IS_OLD_API
|
#ifdef MPC_IS_OLD_API
|
||||||
#include <mpcdec/mpcdec.h>
|
#include <mpcdec/mpcdec.h>
|
||||||
@ -323,18 +324,17 @@ mpcdec_get_file_duration(struct input_stream *is)
|
|||||||
return total_time;
|
return total_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
mpcdec_stream_tag(struct input_stream *is)
|
mpcdec_scan_stream(struct input_stream *is,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
float total_time = mpcdec_get_file_duration(is);
|
float total_time = mpcdec_get_file_duration(is);
|
||||||
struct tag *tag;
|
|
||||||
|
|
||||||
if (total_time < 0)
|
if (total_time < 0)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
tag = tag_new();
|
tag_handler_invoke_duration(handler, handler_ctx, total_time);
|
||||||
tag->time = total_time;
|
return true;
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const mpcdec_suffixes[] = { "mpc", NULL };
|
static const char *const mpcdec_suffixes[] = { "mpc", NULL };
|
||||||
@ -342,6 +342,6 @@ static const char *const mpcdec_suffixes[] = { "mpc", NULL };
|
|||||||
const struct decoder_plugin mpcdec_decoder_plugin = {
|
const struct decoder_plugin mpcdec_decoder_plugin = {
|
||||||
.name = "mpcdec",
|
.name = "mpcdec",
|
||||||
.stream_decode = mpcdec_decode,
|
.stream_decode = mpcdec_decode,
|
||||||
.stream_tag = mpcdec_stream_tag,
|
.scan_stream = mpcdec_scan_stream,
|
||||||
.suffixes = mpcdec_suffixes,
|
.suffixes = mpcdec_suffixes,
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "config.h" /* must be first for large file support */
|
#include "config.h" /* must be first for large file support */
|
||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
@ -192,41 +193,40 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
|
|||||||
mpg123_delete(handle);
|
mpg123_delete(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
mpd_mpg123_tag_dup(const char *path_fs)
|
mpd_mpg123_scan_file(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
struct audio_format audio_format;
|
struct audio_format audio_format;
|
||||||
mpg123_handle *handle;
|
mpg123_handle *handle;
|
||||||
int error;
|
int error;
|
||||||
off_t num_samples;
|
off_t num_samples;
|
||||||
struct tag *tag;
|
|
||||||
|
|
||||||
handle = mpg123_new(NULL, &error);
|
handle = mpg123_new(NULL, &error);
|
||||||
if (handle == NULL) {
|
if (handle == NULL) {
|
||||||
g_warning("mpg123_new() failed: %s",
|
g_warning("mpg123_new() failed: %s",
|
||||||
mpg123_plain_strerror(error));
|
mpg123_plain_strerror(error));
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mpd_mpg123_open(handle, path_fs, &audio_format)) {
|
if (!mpd_mpg123_open(handle, path_fs, &audio_format)) {
|
||||||
mpg123_delete(handle);
|
mpg123_delete(handle);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_samples = mpg123_length(handle);
|
num_samples = mpg123_length(handle);
|
||||||
if (num_samples <= 0) {
|
if (num_samples <= 0) {
|
||||||
mpg123_delete(handle);
|
mpg123_delete(handle);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = tag_new();
|
|
||||||
|
|
||||||
tag->time = num_samples / audio_format.sample_rate;
|
|
||||||
|
|
||||||
/* ID3 tag support not yet implemented */
|
/* ID3 tag support not yet implemented */
|
||||||
|
|
||||||
mpg123_delete(handle);
|
mpg123_delete(handle);
|
||||||
return tag;
|
|
||||||
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
|
num_samples / audio_format.sample_rate);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const mpg123_suffixes[] = {
|
static const char *const mpg123_suffixes[] = {
|
||||||
@ -240,6 +240,6 @@ const struct decoder_plugin mpg123_decoder_plugin = {
|
|||||||
.finish = mpd_mpg123_finish,
|
.finish = mpd_mpg123_finish,
|
||||||
.file_decode = mpd_mpg123_file_decode,
|
.file_decode = mpd_mpg123_file_decode,
|
||||||
/* streaming not yet implemented */
|
/* streaming not yet implemented */
|
||||||
.tag_dup = mpd_mpg123_tag_dup,
|
.scan_file = mpd_mpg123_scan_file,
|
||||||
.suffixes = mpg123_suffixes,
|
.suffixes = mpg123_suffixes,
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "../decoder_api.h"
|
#include "../decoder_api.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -336,8 +337,9 @@ sidplay_file_decode(struct decoder *decoder, const char *path_fs)
|
|||||||
} while (cmd != DECODE_COMMAND_STOP);
|
} while (cmd != DECODE_COMMAND_STOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
sidplay_tag_dup(const char *path_fs)
|
sidplay_scan_file(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
int song_num=get_song_num(path_fs);
|
int song_num=get_song_num(path_fs);
|
||||||
char *path_container=get_container_name(path_fs);
|
char *path_container=get_container_name(path_fs);
|
||||||
@ -345,10 +347,9 @@ sidplay_tag_dup(const char *path_fs)
|
|||||||
SidTune tune(path_container, NULL, true);
|
SidTune tune(path_container, NULL, true);
|
||||||
g_free(path_container);
|
g_free(path_container);
|
||||||
if (!tune)
|
if (!tune)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
const SidTuneInfo &info = tune.getInfo();
|
const SidTuneInfo &info = tune.getInfo();
|
||||||
struct tag *tag = tag_new();
|
|
||||||
|
|
||||||
/* title */
|
/* title */
|
||||||
const char *title;
|
const char *title;
|
||||||
@ -360,25 +361,28 @@ sidplay_tag_dup(const char *path_fs)
|
|||||||
if(info.songs>1) {
|
if(info.songs>1) {
|
||||||
char *tag_title=g_strdup_printf("%s (%d/%d)",
|
char *tag_title=g_strdup_printf("%s (%d/%d)",
|
||||||
title, song_num, info.songs);
|
title, song_num, info.songs);
|
||||||
tag_add_item(tag, TAG_TITLE, tag_title);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_TITLE, tag_title);
|
||||||
g_free(tag_title);
|
g_free(tag_title);
|
||||||
} else
|
} else
|
||||||
tag_add_item(tag, TAG_TITLE, title);
|
tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, title);
|
||||||
|
|
||||||
/* artist */
|
/* artist */
|
||||||
if (info.numberOfInfoStrings > 1 && info.infoString[1] != NULL)
|
if (info.numberOfInfoStrings > 1 && info.infoString[1] != NULL)
|
||||||
tag_add_item(tag, TAG_ARTIST, info.infoString[1]);
|
tag_handler_invoke_tag(handler, handler_ctx, TAG_ARTIST,
|
||||||
|
info.infoString[1]);
|
||||||
|
|
||||||
/* track */
|
/* track */
|
||||||
char *track=g_strdup_printf("%d", song_num);
|
char *track=g_strdup_printf("%d", song_num);
|
||||||
tag_add_item(tag, TAG_TRACK, track);
|
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track);
|
||||||
g_free(track);
|
g_free(track);
|
||||||
|
|
||||||
/* time */
|
/* time */
|
||||||
int song_len=get_song_length(path_fs);
|
int song_len=get_song_length(path_fs);
|
||||||
if(song_len!=-1) tag->time=song_len;
|
if (song_len >= 0)
|
||||||
|
tag_handler_invoke_duration(handler, handler_ctx, song_len);
|
||||||
|
|
||||||
return tag;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
@ -421,7 +425,7 @@ const struct decoder_plugin sidplay_decoder_plugin = {
|
|||||||
sidplay_finish,
|
sidplay_finish,
|
||||||
NULL, /* stream_decode() */
|
NULL, /* stream_decode() */
|
||||||
sidplay_file_decode,
|
sidplay_file_decode,
|
||||||
sidplay_tag_dup,
|
sidplay_scan_file,
|
||||||
NULL, /* stream_tag() */
|
NULL, /* stream_tag() */
|
||||||
sidplay_container_scan,
|
sidplay_container_scan,
|
||||||
sidplay_suffixes,
|
sidplay_suffixes,
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#include <sndfile.h>
|
#include <sndfile.h>
|
||||||
|
|
||||||
@ -172,44 +173,47 @@ sndfile_stream_decode(struct decoder *decoder, struct input_stream *is)
|
|||||||
sf_close(sf);
|
sf_close(sf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
sndfile_tag_dup(const char *path_fs)
|
sndfile_scan_file(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
SNDFILE *sf;
|
SNDFILE *sf;
|
||||||
SF_INFO info;
|
SF_INFO info;
|
||||||
struct tag *tag;
|
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
info.format = 0;
|
info.format = 0;
|
||||||
|
|
||||||
sf = sf_open(path_fs, SFM_READ, &info);
|
sf = sf_open(path_fs, SFM_READ, &info);
|
||||||
if (sf == NULL)
|
if (sf == NULL)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
if (!audio_valid_sample_rate(info.samplerate)) {
|
if (!audio_valid_sample_rate(info.samplerate)) {
|
||||||
sf_close(sf);
|
sf_close(sf);
|
||||||
g_warning("Invalid sample rate in %s\n", path_fs);
|
g_warning("Invalid sample rate in %s\n", path_fs);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = tag_new();
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
tag->time = info.frames / info.samplerate;
|
info.frames / info.samplerate);
|
||||||
|
|
||||||
p = sf_get_string(sf, SF_STR_TITLE);
|
p = sf_get_string(sf, SF_STR_TITLE);
|
||||||
if (p != NULL)
|
if (p != NULL)
|
||||||
tag_add_item(tag, TAG_TITLE, p);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_TITLE, p);
|
||||||
|
|
||||||
p = sf_get_string(sf, SF_STR_ARTIST);
|
p = sf_get_string(sf, SF_STR_ARTIST);
|
||||||
if (p != NULL)
|
if (p != NULL)
|
||||||
tag_add_item(tag, TAG_ARTIST, p);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_ARTIST, p);
|
||||||
|
|
||||||
p = sf_get_string(sf, SF_STR_DATE);
|
p = sf_get_string(sf, SF_STR_DATE);
|
||||||
if (p != NULL)
|
if (p != NULL)
|
||||||
tag_add_item(tag, TAG_DATE, p);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_DATE, p);
|
||||||
|
|
||||||
sf_close(sf);
|
sf_close(sf);
|
||||||
|
|
||||||
return tag;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const sndfile_suffixes[] = {
|
static const char *const sndfile_suffixes[] = {
|
||||||
@ -245,7 +249,7 @@ static const char *const sndfile_mime_types[] = {
|
|||||||
const struct decoder_plugin sndfile_decoder_plugin = {
|
const struct decoder_plugin sndfile_decoder_plugin = {
|
||||||
.name = "sndfile",
|
.name = "sndfile",
|
||||||
.stream_decode = sndfile_stream_decode,
|
.stream_decode = sndfile_stream_decode,
|
||||||
.tag_dup = sndfile_tag_dup,
|
.scan_file = sndfile_scan_file,
|
||||||
.suffixes = sndfile_suffixes,
|
.suffixes = sndfile_suffixes,
|
||||||
.mime_types = sndfile_mime_types,
|
.mime_types = sndfile_mime_types,
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "vorbis_comments.h"
|
#include "vorbis_comments.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
#include "tag_table.h"
|
#include "tag_table.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
#include "replay_gain_info.h"
|
#include "replay_gain_info.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
@ -79,14 +80,15 @@ vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments)
|
|||||||
* the comment value into the tag.
|
* the comment value into the tag.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
vorbis_copy_comment(struct tag *tag, const char *comment,
|
vorbis_copy_comment(const char *comment,
|
||||||
const char *name, enum tag_type tag_type)
|
const char *name, enum tag_type tag_type,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
const char *value;
|
const char *value;
|
||||||
|
|
||||||
value = vorbis_comment_value(comment, name);
|
value = vorbis_comment_value(comment, name);
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
tag_add_item(tag, tag_type, value);
|
tag_handler_invoke_tag(handler, handler_ctx, tag_type, value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,27 +103,36 @@ static const struct tag_table vorbis_tags[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vorbis_parse_comment(struct tag *tag, const char *comment)
|
vorbis_scan_comment(const char *comment,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
assert(tag != NULL);
|
|
||||||
|
|
||||||
for (const struct tag_table *i = vorbis_tags; i->name != NULL; ++i)
|
for (const struct tag_table *i = vorbis_tags; i->name != NULL; ++i)
|
||||||
if (vorbis_copy_comment(tag, comment, i->name, i->type))
|
if (vorbis_copy_comment(comment, i->name, i->type,
|
||||||
|
handler, handler_ctx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
||||||
if (vorbis_copy_comment(tag, comment,
|
if (vorbis_copy_comment(comment,
|
||||||
tag_item_names[i], i))
|
tag_item_names[i], i,
|
||||||
|
handler, handler_ctx))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vorbis_comments_scan(char **comments,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
|
{
|
||||||
|
while (*comments)
|
||||||
|
vorbis_scan_comment(*comments++,
|
||||||
|
handler, handler_ctx);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
struct tag *
|
struct tag *
|
||||||
vorbis_comments_to_tag(char **comments)
|
vorbis_comments_to_tag(char **comments)
|
||||||
{
|
{
|
||||||
struct tag *tag = tag_new();
|
struct tag *tag = tag_new();
|
||||||
|
vorbis_comments_scan(comments, &add_tag_handler, tag);
|
||||||
while (*comments)
|
|
||||||
vorbis_parse_comment(tag, *comments++);
|
|
||||||
|
|
||||||
if (tag_is_empty(tag)) {
|
if (tag_is_empty(tag)) {
|
||||||
tag_free(tag);
|
tag_free(tag);
|
||||||
|
@ -25,10 +25,15 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
struct replay_gain_info;
|
struct replay_gain_info;
|
||||||
|
struct tag_handler;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments);
|
vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments);
|
||||||
|
|
||||||
|
void
|
||||||
|
vorbis_comments_scan(char **comments,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx);
|
||||||
|
|
||||||
struct tag *
|
struct tag *
|
||||||
vorbis_comments_to_tag(char **comments);
|
vorbis_comments_to_tag(char **comments);
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "_ogg_common.h"
|
#include "_ogg_common.h"
|
||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
#include "uri.h"
|
#include "uri.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#ifndef HAVE_TREMOR
|
#ifndef HAVE_TREMOR
|
||||||
#define OV_EXCLUDE_STATIC_CALLBACKS
|
#define OV_EXCLUDE_STATIC_CALLBACKS
|
||||||
@ -268,24 +269,24 @@ vorbis_stream_decode(struct decoder *decoder,
|
|||||||
ov_clear(&vf);
|
ov_clear(&vf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
vorbis_stream_tag(struct input_stream *is)
|
vorbis_scan_stream(struct input_stream *is,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
struct vorbis_input_stream vis;
|
struct vorbis_input_stream vis;
|
||||||
OggVorbis_File vf;
|
OggVorbis_File vf;
|
||||||
|
|
||||||
if (!vorbis_is_open(&vis, &vf, NULL, is))
|
if (!vorbis_is_open(&vis, &vf, NULL, is))
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
struct tag *tag = vorbis_comments_to_tag(ov_comment(&vf, -1)->user_comments);
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
|
(int)(ov_time_total(&vf, -1) + 0.5));
|
||||||
|
|
||||||
if (tag == NULL)
|
vorbis_comments_scan(ov_comment(&vf, -1)->user_comments,
|
||||||
tag = tag_new();
|
handler, handler_ctx);
|
||||||
tag->time = (int)(ov_time_total(&vf, -1) + 0.5);
|
|
||||||
|
|
||||||
ov_clear(&vf);
|
ov_clear(&vf);
|
||||||
|
return true;
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const vorbis_suffixes[] = {
|
static const char *const vorbis_suffixes[] = {
|
||||||
@ -307,7 +308,7 @@ static const char *const vorbis_mime_types[] = {
|
|||||||
const struct decoder_plugin vorbis_decoder_plugin = {
|
const struct decoder_plugin vorbis_decoder_plugin = {
|
||||||
.name = "vorbis",
|
.name = "vorbis",
|
||||||
.stream_decode = vorbis_stream_decode,
|
.stream_decode = vorbis_stream_decode,
|
||||||
.stream_tag = vorbis_stream_tag,
|
.scan_stream = vorbis_scan_stream,
|
||||||
.suffixes = vorbis_suffixes,
|
.suffixes = vorbis_suffixes,
|
||||||
.mime_types = vorbis_mime_types
|
.mime_types = vorbis_mime_types
|
||||||
};
|
};
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "audio_check.h"
|
#include "audio_check.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#include <wavpack/wavpack.h>
|
#include <wavpack/wavpack.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
@ -275,11 +276,11 @@ wavpack_replaygain(struct replay_gain_info *replay_gain_info,
|
|||||||
/*
|
/*
|
||||||
* Reads metainfo from the specified file.
|
* Reads metainfo from the specified file.
|
||||||
*/
|
*/
|
||||||
static struct tag *
|
static bool
|
||||||
wavpack_tagdup(const char *fname)
|
wavpack_scan_file(const char *fname,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
WavpackContext *wpc;
|
WavpackContext *wpc;
|
||||||
struct tag *tag;
|
|
||||||
char error[ERRORLEN];
|
char error[ERRORLEN];
|
||||||
char *s;
|
char *s;
|
||||||
int size, allocated_size;
|
int size, allocated_size;
|
||||||
@ -290,12 +291,12 @@ wavpack_tagdup(const char *fname)
|
|||||||
"failed to open WavPack file \"%s\": %s\n",
|
"failed to open WavPack file \"%s\": %s\n",
|
||||||
fname, error
|
fname, error
|
||||||
);
|
);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = tag_new();
|
tag_handler_invoke_duration(handler, handler_ctx,
|
||||||
tag->time = WavpackGetNumSamples(wpc);
|
WavpackGetNumSamples(wpc) /
|
||||||
tag->time /= WavpackGetSampleRate(wpc);
|
WavpackGetSampleRate(wpc));
|
||||||
|
|
||||||
allocated_size = 0;
|
allocated_size = 0;
|
||||||
s = NULL;
|
s = NULL;
|
||||||
@ -315,7 +316,8 @@ wavpack_tagdup(const char *fname)
|
|||||||
}
|
}
|
||||||
|
|
||||||
WavpackGetTagItem(wpc, tagtypes[i].name, s, size);
|
WavpackGetTagItem(wpc, tagtypes[i].name, s, size);
|
||||||
tag_add_item(tag, tagtypes[i].type, s);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
tagtypes[i].type, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +325,7 @@ wavpack_tagdup(const char *fname)
|
|||||||
|
|
||||||
WavpackCloseFile(wpc);
|
WavpackCloseFile(wpc);
|
||||||
|
|
||||||
return tag;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -576,7 +578,7 @@ const struct decoder_plugin wavpack_decoder_plugin = {
|
|||||||
.name = "wavpack",
|
.name = "wavpack",
|
||||||
.stream_decode = wavpack_streamdecode,
|
.stream_decode = wavpack_streamdecode,
|
||||||
.file_decode = wavpack_filedecode,
|
.file_decode = wavpack_filedecode,
|
||||||
.tag_dup = wavpack_tagdup,
|
.scan_file = wavpack_scan_file,
|
||||||
.suffixes = wavpack_suffixes,
|
.suffixes = wavpack_suffixes,
|
||||||
.mime_types = wavpack_mime_types
|
.mime_types = wavpack_mime_types
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
@ -111,25 +112,26 @@ wildmidi_file_decode(struct decoder *decoder, const char *path_fs)
|
|||||||
WildMidi_Close(wm);
|
WildMidi_Close(wm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static bool
|
||||||
wildmidi_tag_dup(const char *path_fs)
|
wildmidi_scan_file(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
midi *wm = WildMidi_Open(path_fs);
|
midi *wm = WildMidi_Open(path_fs);
|
||||||
if (wm == NULL)
|
if (wm == NULL)
|
||||||
return NULL;
|
return false;
|
||||||
|
|
||||||
const struct _WM_Info *info = WildMidi_GetInfo(wm);
|
const struct _WM_Info *info = WildMidi_GetInfo(wm);
|
||||||
if (info == NULL) {
|
if (info == NULL) {
|
||||||
WildMidi_Close(wm);
|
WildMidi_Close(wm);
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tag *tag = tag_new();
|
int duration = info->approx_total_samples / WILDMIDI_SAMPLE_RATE;
|
||||||
tag->time = info->approx_total_samples / WILDMIDI_SAMPLE_RATE;
|
tag_handler_invoke_duration(handler, handler_ctx, duration);
|
||||||
|
|
||||||
WildMidi_Close(wm);
|
WildMidi_Close(wm);
|
||||||
|
|
||||||
return tag;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const wildmidi_suffixes[] = {
|
static const char *const wildmidi_suffixes[] = {
|
||||||
@ -142,6 +144,6 @@ const struct decoder_plugin wildmidi_decoder_plugin = {
|
|||||||
.init = wildmidi_init,
|
.init = wildmidi_init,
|
||||||
.finish = wildmidi_finish,
|
.finish = wildmidi_finish,
|
||||||
.file_decode = wildmidi_file_decode,
|
.file_decode = wildmidi_file_decode,
|
||||||
.tag_dup = wildmidi_tag_dup,
|
.scan_file = wildmidi_scan_file,
|
||||||
.suffixes = wildmidi_suffixes,
|
.suffixes = wildmidi_suffixes,
|
||||||
};
|
};
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
struct config_param;
|
struct config_param;
|
||||||
struct input_stream;
|
struct input_stream;
|
||||||
struct tag;
|
struct tag;
|
||||||
|
struct tag_handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opaque handle which the decoder plugin passes to the functions in
|
* Opaque handle which the decoder plugin passes to the functions in
|
||||||
@ -70,18 +71,22 @@ struct decoder_plugin {
|
|||||||
void (*file_decode)(struct decoder *decoder, const char *path_fs);
|
void (*file_decode)(struct decoder *decoder, const char *path_fs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the tags of a local file.
|
* Scan metadata of a file.
|
||||||
*
|
*
|
||||||
* @return NULL if the operation has failed
|
* @return false if the operation has failed
|
||||||
*/
|
*/
|
||||||
struct tag *(*tag_dup)(const char *path_fs);
|
bool (*scan_file)(const char *path_fs,
|
||||||
|
const struct tag_handler *handler,
|
||||||
|
void *handler_ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the tags of a stream.
|
* Scan metadata of a file.
|
||||||
*
|
*
|
||||||
* @return NULL if the operation has failed
|
* @return false if the operation has failed
|
||||||
*/
|
*/
|
||||||
struct tag *(*stream_tag)(struct input_stream *is);
|
bool (*scan_stream)(struct input_stream *is,
|
||||||
|
const struct tag_handler *handler,
|
||||||
|
void *handler_ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return a "virtual" filename for subtracks in
|
* @brief Return a "virtual" filename for subtracks in
|
||||||
@ -150,25 +155,28 @@ decoder_plugin_file_decode(const struct decoder_plugin *plugin,
|
|||||||
/**
|
/**
|
||||||
* Read the tag of a file.
|
* Read the tag of a file.
|
||||||
*/
|
*/
|
||||||
static inline struct tag *
|
static inline bool
|
||||||
decoder_plugin_tag_dup(const struct decoder_plugin *plugin,
|
decoder_plugin_scan_file(const struct decoder_plugin *plugin,
|
||||||
const char *path_fs)
|
const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
return plugin->tag_dup != NULL
|
return plugin->scan_file != NULL
|
||||||
? plugin->tag_dup(path_fs)
|
? plugin->scan_file(path_fs, handler, handler_ctx)
|
||||||
: NULL;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the tag of a stream.
|
* Read the tag of a stream.
|
||||||
*/
|
*/
|
||||||
static inline struct tag *
|
static inline bool
|
||||||
decoder_plugin_stream_tag(const struct decoder_plugin *plugin,
|
decoder_plugin_scan_stream(const struct decoder_plugin *plugin,
|
||||||
struct input_stream *is)
|
struct input_stream *is,
|
||||||
|
const struct tag_handler *handler,
|
||||||
|
void *handler_ctx)
|
||||||
{
|
{
|
||||||
return plugin->stream_tag != NULL
|
return plugin->scan_stream != NULL
|
||||||
? plugin->stream_tag(is)
|
? plugin->scan_stream(is, handler, handler_ctx)
|
||||||
: NULL;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "tag_ape.h"
|
#include "tag_ape.h"
|
||||||
#include "tag_id3.h"
|
#include "tag_id3.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
#include "input_stream.h"
|
#include "input_stream.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
@ -136,12 +137,16 @@ song_file_update(struct song *song)
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
/* load file tag */
|
/* load file tag */
|
||||||
song->tag = decoder_plugin_tag_dup(plugin, path_fs);
|
song->tag = tag_new();
|
||||||
if (song->tag != NULL)
|
if (decoder_plugin_scan_file(plugin, path_fs,
|
||||||
|
&add_tag_handler, song->tag))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
tag_free(song->tag);
|
||||||
|
song->tag = NULL;
|
||||||
|
|
||||||
/* fall back to stream tag */
|
/* fall back to stream tag */
|
||||||
if (plugin->stream_tag != NULL) {
|
if (plugin->scan_stream != NULL) {
|
||||||
/* open the input_stream (if not already
|
/* open the input_stream (if not already
|
||||||
open) */
|
open) */
|
||||||
if (is == NULL) {
|
if (is == NULL) {
|
||||||
@ -153,11 +158,15 @@ song_file_update(struct song *song)
|
|||||||
|
|
||||||
/* now try the stream_tag() method */
|
/* now try the stream_tag() method */
|
||||||
if (is != NULL) {
|
if (is != NULL) {
|
||||||
song->tag = decoder_plugin_stream_tag(plugin,
|
song->tag = tag_new();
|
||||||
is);
|
if (decoder_plugin_scan_stream(plugin, is,
|
||||||
if (song->tag != NULL)
|
&add_tag_handler,
|
||||||
|
song->tag))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
tag_free(song->tag);
|
||||||
|
song->tag = NULL;
|
||||||
|
|
||||||
input_stream_lock_seek(is, 0, SEEK_SET, NULL);
|
input_stream_lock_seek(is, 0, SEEK_SET, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "tag_ape.h"
|
#include "tag_ape.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
#include "tag_table.h"
|
#include "tag_table.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
#include "ape.h"
|
#include "ape.h"
|
||||||
|
|
||||||
static const struct tag_table ape_tags[] = {
|
static const struct tag_table ape_tags[] = {
|
||||||
@ -39,20 +40,18 @@ tag_ape_name_parse(const char *name)
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct tag *
|
static void
|
||||||
tag_ape_import_item(struct tag *tag, unsigned long flags,
|
tag_ape_import_item(unsigned long flags,
|
||||||
const char *key, const char *value, size_t value_length)
|
const char *key, const char *value, size_t value_length,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
/* we only care about utf-8 text tags */
|
/* we only care about utf-8 text tags */
|
||||||
if ((flags & (0x3 << 1)) != 0)
|
if ((flags & (0x3 << 1)) != 0)
|
||||||
return tag;
|
return;
|
||||||
|
|
||||||
enum tag_type type = tag_ape_name_parse(key);
|
enum tag_type type = tag_ape_name_parse(key);
|
||||||
if (type == TAG_NUM_OF_ITEM_TYPES)
|
if (type == TAG_NUM_OF_ITEM_TYPES)
|
||||||
return tag;
|
return;
|
||||||
|
|
||||||
if (tag == NULL)
|
|
||||||
tag = tag_new();
|
|
||||||
|
|
||||||
const char *end = value + value_length;
|
const char *end = value + value_length;
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -60,20 +59,22 @@ tag_ape_import_item(struct tag *tag, unsigned long flags,
|
|||||||
const char *n = memchr(value, 0, end - value);
|
const char *n = memchr(value, 0, end - value);
|
||||||
if (n != NULL) {
|
if (n != NULL) {
|
||||||
if (n > value)
|
if (n > value)
|
||||||
tag_add_item_n(tag, type, value, n - value);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
type, value);
|
||||||
value = n + 1;
|
value = n + 1;
|
||||||
} else {
|
} else {
|
||||||
if (end > value)
|
char *p = g_strndup(value, end - value);
|
||||||
tag_add_item_n(tag, type, value, end - value);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
type, p);
|
||||||
|
g_free(p);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tag_ape_ctx {
|
struct tag_ape_ctx {
|
||||||
struct tag *tag;
|
const struct tag_handler *handler;
|
||||||
|
void *handler_ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -82,16 +83,31 @@ tag_ape_callback(unsigned long flags, const char *key,
|
|||||||
{
|
{
|
||||||
struct tag_ape_ctx *ctx = _ctx;
|
struct tag_ape_ctx *ctx = _ctx;
|
||||||
|
|
||||||
ctx->tag = tag_ape_import_item(ctx->tag, flags, key,
|
tag_ape_import_item(flags, key, value, value_length,
|
||||||
value, value_length);
|
ctx->handler, ctx->handler_ctx);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tag *
|
bool
|
||||||
tag_ape_load(const char *file)
|
tag_ape_scan2(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
struct tag_ape_ctx ctx = { .tag = NULL };
|
struct tag_ape_ctx ctx = {
|
||||||
|
.handler = handler,
|
||||||
|
.handler_ctx = handler_ctx,
|
||||||
|
};
|
||||||
|
|
||||||
tag_ape_scan(file, tag_ape_callback, &ctx);
|
return tag_ape_scan(path_fs, tag_ape_callback, &ctx);
|
||||||
return ctx.tag;
|
}
|
||||||
|
|
||||||
|
struct tag *
|
||||||
|
tag_ape_load(const char *path_fs)
|
||||||
|
{
|
||||||
|
struct tag *tag = tag_new();
|
||||||
|
if (!tag_ape_scan2(path_fs, &add_tag_handler, tag)) {
|
||||||
|
tag_free(tag);
|
||||||
|
tag = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,19 @@
|
|||||||
#ifndef MPD_TAG_APE_H
|
#ifndef MPD_TAG_APE_H
|
||||||
#define MPD_TAG_APE_H
|
#define MPD_TAG_APE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct tag_handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scan the APE tags of a file.
|
||||||
|
*
|
||||||
|
* @param path_fs the path of the file in filesystem encoding
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
tag_ape_scan2(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the APE tag from a file.
|
* Loads the APE tag from a file.
|
||||||
*
|
*
|
||||||
|
43
src/tag_handler.c
Normal file
43
src/tag_handler.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_tag_duration(unsigned seconds, void *ctx)
|
||||||
|
{
|
||||||
|
struct tag *tag = ctx;
|
||||||
|
|
||||||
|
tag->time = seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_tag_tag(enum tag_type type, const char *value, void *ctx)
|
||||||
|
{
|
||||||
|
struct tag *tag = ctx;
|
||||||
|
|
||||||
|
tag_add_item(tag, type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct tag_handler add_tag_handler = {
|
||||||
|
.duration = add_tag_duration,
|
||||||
|
.tag = add_tag_tag,
|
||||||
|
};
|
||||||
|
|
76
src/tag_handler.h
Normal file
76
src/tag_handler.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPD_TAG_HANDLER_H
|
||||||
|
#define MPD_TAG_HANDLER_H
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
#include "tag.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback table for receiving metadata of a song.
|
||||||
|
*/
|
||||||
|
struct tag_handler {
|
||||||
|
/**
|
||||||
|
* Declare the duration of a song, in seconds. Do not call
|
||||||
|
* this when the duration could not be determined, because
|
||||||
|
* there is no magic value for "unknown duration".
|
||||||
|
*/
|
||||||
|
void (*duration)(unsigned seconds, void *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A tag has been read.
|
||||||
|
*
|
||||||
|
* @param the value of the tag; the pointer will become
|
||||||
|
* invalid after returning
|
||||||
|
*/
|
||||||
|
void (*tag)(enum tag_type type, const char *value, void *ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
tag_handler_invoke_duration(const struct tag_handler *handler, void *ctx,
|
||||||
|
unsigned seconds)
|
||||||
|
{
|
||||||
|
assert(handler != NULL);
|
||||||
|
|
||||||
|
if (handler->duration != NULL)
|
||||||
|
handler->duration(seconds, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
tag_handler_invoke_tag(const struct tag_handler *handler, void *ctx,
|
||||||
|
enum tag_type type, const char *value)
|
||||||
|
{
|
||||||
|
assert(handler != NULL);
|
||||||
|
assert((unsigned)type < TAG_NUM_OF_ITEM_TYPES);
|
||||||
|
assert(value != NULL);
|
||||||
|
|
||||||
|
if (handler->tag != NULL)
|
||||||
|
handler->tag(type, value, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This #tag_handler implementation adds tag values to a #tag object
|
||||||
|
* (casted from the context pointer).
|
||||||
|
*/
|
||||||
|
extern const struct tag_handler add_tag_handler;
|
||||||
|
|
||||||
|
#endif
|
136
src/tag_id3.c
136
src/tag_id3.c
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "tag_id3.h"
|
#include "tag_id3.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
#include "tag_table.h"
|
#include "tag_table.h"
|
||||||
#include "tag.h"
|
#include "tag.h"
|
||||||
#include "riff.h"
|
#include "riff.h"
|
||||||
@ -127,9 +128,9 @@ import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4)
|
|||||||
* - string list
|
* - string list
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag,
|
tag_id3_import_text_frame(struct id3_tag *tag, const struct id3_frame *frame,
|
||||||
const struct id3_frame *frame,
|
enum tag_type type,
|
||||||
enum tag_type type)
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
id3_ucs4_t const *ucs4;
|
id3_ucs4_t const *ucs4;
|
||||||
id3_utf8_t *utf8;
|
id3_utf8_t *utf8;
|
||||||
@ -165,7 +166,8 @@ tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag,
|
|||||||
if (utf8 == NULL)
|
if (utf8 == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
tag_add_item(dest, type, (char *)utf8);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
type, (const char *)utf8);
|
||||||
g_free(utf8);
|
g_free(utf8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,13 +177,14 @@ tag_id3_import_text_frame(struct tag *dest, struct id3_tag *tag,
|
|||||||
* 4.2). This is a wrapper for tag_id3_import_text_frame().
|
* 4.2). This is a wrapper for tag_id3_import_text_frame().
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
|
tag_id3_import_text(struct id3_tag *tag, const char *id, enum tag_type type,
|
||||||
enum tag_type type)
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
const struct id3_frame *frame;
|
const struct id3_frame *frame;
|
||||||
for (unsigned i = 0;
|
for (unsigned i = 0;
|
||||||
(frame = id3_tag_findframe(tag, id, i)) != NULL; ++i)
|
(frame = id3_tag_findframe(tag, id, i)) != NULL; ++i)
|
||||||
tag_id3_import_text_frame(dest, tag, frame, type);
|
tag_id3_import_text_frame(tag, frame, type,
|
||||||
|
handler, handler_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -194,9 +197,10 @@ tag_id3_import_text(struct tag *dest, struct id3_tag *tag, const char *id,
|
|||||||
* - full string (we use this one)
|
* - full string (we use this one)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag,
|
tag_id3_import_comment_frame(struct id3_tag *tag,
|
||||||
const struct id3_frame *frame,
|
const struct id3_frame *frame, enum tag_type type,
|
||||||
enum tag_type type)
|
const struct tag_handler *handler,
|
||||||
|
void *handler_ctx)
|
||||||
{
|
{
|
||||||
id3_ucs4_t const *ucs4;
|
id3_ucs4_t const *ucs4;
|
||||||
id3_utf8_t *utf8;
|
id3_utf8_t *utf8;
|
||||||
@ -218,7 +222,7 @@ tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag,
|
|||||||
if (utf8 == NULL)
|
if (utf8 == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tag_add_item(dest, type, (char *)utf8);
|
tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8);
|
||||||
g_free(utf8);
|
g_free(utf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,13 +231,14 @@ tag_id3_import_comment_frame(struct tag *dest, struct id3_tag *tag,
|
|||||||
* wrapper for tag_id3_import_comment_frame().
|
* wrapper for tag_id3_import_comment_frame().
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tag_id3_import_comment(struct tag *dest, struct id3_tag *tag, const char *id,
|
tag_id3_import_comment(struct id3_tag *tag, const char *id, enum tag_type type,
|
||||||
enum tag_type type)
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
const struct id3_frame *frame;
|
const struct id3_frame *frame;
|
||||||
for (unsigned i = 0;
|
for (unsigned i = 0;
|
||||||
(frame = id3_tag_findframe(tag, id, i)) != NULL; ++i)
|
(frame = id3_tag_findframe(tag, id, i)) != NULL; ++i)
|
||||||
tag_id3_import_comment_frame(dest, tag, frame, type);
|
tag_id3_import_comment_frame(tag, frame, type,
|
||||||
|
handler, handler_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -260,7 +265,9 @@ tag_id3_parse_txxx_name(const char *name)
|
|||||||
* Import all known MusicBrainz tags from TXXX frames.
|
* Import all known MusicBrainz tags from TXXX frames.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tag_id3_import_musicbrainz(struct tag *mpd_tag, struct id3_tag *id3_tag)
|
tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
|
||||||
|
const struct tag_handler *handler,
|
||||||
|
void *handler_ctx)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0;; ++i) {
|
for (unsigned i = 0;; ++i) {
|
||||||
const struct id3_frame *frame;
|
const struct id3_frame *frame;
|
||||||
@ -285,7 +292,8 @@ tag_id3_import_musicbrainz(struct tag *mpd_tag, struct id3_tag *id3_tag)
|
|||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
tag_add_item(mpd_tag, type, (const char*)value);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
type, (const char*)value);
|
||||||
free(value);
|
free(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,7 +302,8 @@ tag_id3_import_musicbrainz(struct tag *mpd_tag, struct id3_tag *id3_tag)
|
|||||||
* Imports the MusicBrainz TrackId from the UFID tag.
|
* Imports the MusicBrainz TrackId from the UFID tag.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
tag_id3_import_ufid(struct tag *mpd_tag, struct id3_tag *id3_tag)
|
tag_id3_import_ufid(struct id3_tag *id3_tag,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0;; ++i) {
|
for (unsigned i = 0;; ++i) {
|
||||||
const struct id3_frame *frame;
|
const struct id3_frame *frame;
|
||||||
@ -324,35 +333,54 @@ tag_id3_import_ufid(struct tag *mpd_tag, struct id3_tag *id3_tag)
|
|||||||
if (value == NULL || length == 0)
|
if (value == NULL || length == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
tag_add_item_n(mpd_tag, TAG_MUSICBRAINZ_TRACKID,
|
char *p = g_strndup((const char *)value, length);
|
||||||
(const char*)value, length);
|
tag_handler_invoke_tag(handler, handler_ctx,
|
||||||
|
TAG_MUSICBRAINZ_TRACKID, p);
|
||||||
|
g_free(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
scan_id3_tag(struct id3_tag *tag,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
|
{
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_ARTIST, TAG_ARTIST,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST,
|
||||||
|
TAG_ALBUM_ARTIST, handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_ARTIST_SORT,
|
||||||
|
TAG_ARTIST_SORT, handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST_SORT,
|
||||||
|
TAG_ALBUM_ARTIST_SORT, handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_TITLE, TAG_TITLE,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_ALBUM, TAG_ALBUM,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_TRACK, TAG_TRACK,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_YEAR, TAG_DATE,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_GENRE, TAG_GENRE,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_COMPOSER, TAG_COMPOSER,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, "TPE3", TAG_PERFORMER,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler, handler_ctx);
|
||||||
|
tag_id3_import_comment(tag, ID3_FRAME_COMMENT, TAG_COMMENT,
|
||||||
|
handler, handler_ctx);
|
||||||
|
tag_id3_import_text(tag, ID3_FRAME_DISC, TAG_DISC,
|
||||||
|
handler, handler_ctx);
|
||||||
|
|
||||||
|
tag_id3_import_musicbrainz(tag, handler, handler_ctx);
|
||||||
|
tag_id3_import_ufid(tag, handler, handler_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
struct tag *tag_id3_import(struct id3_tag * tag)
|
struct tag *tag_id3_import(struct id3_tag * tag)
|
||||||
{
|
{
|
||||||
struct tag *ret = tag_new();
|
struct tag *ret = tag_new();
|
||||||
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_ARTIST, TAG_ARTIST);
|
scan_id3_tag(tag, &add_tag_handler, ret);
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM_ARTIST,
|
|
||||||
TAG_ALBUM_ARTIST);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_ARTIST_SORT,
|
|
||||||
TAG_ARTIST_SORT);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM_ARTIST_SORT,
|
|
||||||
TAG_ALBUM_ARTIST_SORT);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_TITLE, TAG_TITLE);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM, TAG_ALBUM);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_TRACK, TAG_TRACK);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_YEAR, TAG_DATE);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_GENRE, TAG_GENRE);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_COMPOSER, TAG_COMPOSER);
|
|
||||||
tag_id3_import_text(ret, tag, "TPE3", TAG_PERFORMER);
|
|
||||||
tag_id3_import_text(ret, tag, "TPE4", TAG_PERFORMER);
|
|
||||||
tag_id3_import_comment(ret, tag, ID3_FRAME_COMMENT, TAG_COMMENT);
|
|
||||||
tag_id3_import_text(ret, tag, ID3_FRAME_DISC, TAG_DISC);
|
|
||||||
|
|
||||||
tag_id3_import_musicbrainz(ret, tag);
|
|
||||||
tag_id3_import_ufid(ret, tag);
|
|
||||||
|
|
||||||
if (tag_is_empty(ret)) {
|
if (tag_is_empty(ret)) {
|
||||||
tag_free(ret);
|
tag_free(ret);
|
||||||
@ -510,6 +538,36 @@ tag_id3_riff_aiff_load(FILE *file)
|
|||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
tag_id3_scan(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx)
|
||||||
|
{
|
||||||
|
struct id3_tag *tag;
|
||||||
|
FILE *stream;
|
||||||
|
|
||||||
|
stream = fopen(path_fs, "rb");
|
||||||
|
if (!stream) {
|
||||||
|
g_debug("tag_id3_load: Failed to open file: '%s', %s",
|
||||||
|
path_fs, strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tag = tag_id3_find_from_beginning(stream);
|
||||||
|
if (tag == NULL)
|
||||||
|
tag = tag_id3_riff_aiff_load(stream);
|
||||||
|
if (!tag)
|
||||||
|
tag = tag_id3_find_from_end(stream);
|
||||||
|
|
||||||
|
fclose(stream);
|
||||||
|
|
||||||
|
if (!tag)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
scan_id3_tag(tag, handler, handler_ctx);
|
||||||
|
id3_tag_delete(tag);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct tag *tag_id3_load(const char *file)
|
struct tag *tag_id3_load(const char *file)
|
||||||
{
|
{
|
||||||
struct tag *ret;
|
struct tag *ret;
|
||||||
|
@ -22,9 +22,17 @@
|
|||||||
|
|
||||||
#include "check.h"
|
#include "check.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct tag_handler;
|
||||||
struct tag;
|
struct tag;
|
||||||
|
|
||||||
#ifdef HAVE_ID3TAG
|
#ifdef HAVE_ID3TAG
|
||||||
|
|
||||||
|
bool
|
||||||
|
tag_id3_scan(const char *path_fs,
|
||||||
|
const struct tag_handler *handler, void *handler_ctx);
|
||||||
|
|
||||||
struct id3_tag;
|
struct id3_tag;
|
||||||
struct tag *tag_id3_import(struct id3_tag *);
|
struct tag *tag_id3_import(struct id3_tag *);
|
||||||
|
|
||||||
@ -34,6 +42,14 @@ struct tag *tag_id3_load(const char *file);
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
tag_id3_scan(G_GNUC_UNUSED const char *path_fs,
|
||||||
|
G_GNUC_UNUSED const struct tag_handler *handler,
|
||||||
|
G_GNUC_UNUSED void *handler_ctx)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct tag *
|
static inline struct tag *
|
||||||
tag_id3_load(G_GNUC_UNUSED const char *file)
|
tag_id3_load(G_GNUC_UNUSED const char *file)
|
||||||
{
|
{
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
#include "decoder_plugin.h"
|
#include "decoder_plugin.h"
|
||||||
#include "playlist_list.h"
|
#include "playlist_list.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
|
#include "tag.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
|
|
||||||
#ifdef ENABLE_ARCHIVE
|
#ifdef ENABLE_ARCHIVE
|
||||||
#include "archive_list.h"
|
#include "archive_list.h"
|
||||||
@ -511,7 +513,9 @@ update_container_file( struct directory* directory,
|
|||||||
|
|
||||||
child_path_fs = map_directory_child_fs(contdir, vtrack);
|
child_path_fs = map_directory_child_fs(contdir, vtrack);
|
||||||
|
|
||||||
song->tag = plugin->tag_dup(child_path_fs);
|
song->tag = tag_new();
|
||||||
|
decoder_plugin_scan_file(plugin, child_path_fs,
|
||||||
|
&add_tag_handler, song->tag);
|
||||||
g_free(child_path_fs);
|
g_free(child_path_fs);
|
||||||
|
|
||||||
directory_add_song(contdir, song);
|
directory_add_song(contdir, song);
|
||||||
|
@ -25,9 +25,9 @@
|
|||||||
#include "input_stream.h"
|
#include "input_stream.h"
|
||||||
#include "audio_format.h"
|
#include "audio_format.h"
|
||||||
#include "pcm_volume.h"
|
#include "pcm_volume.h"
|
||||||
#include "tag_pool.h"
|
|
||||||
#include "tag_ape.h"
|
#include "tag_ape.h"
|
||||||
#include "tag_id3.h"
|
#include "tag_id3.h"
|
||||||
|
#include "tag_handler.h"
|
||||||
#include "idle.h"
|
#include "idle.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
@ -134,25 +134,31 @@ decoder_mixramp(G_GNUC_UNUSED struct decoder *decoder,
|
|||||||
g_free(mixramp_end);
|
g_free(mixramp_end);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static bool empty = true;
|
||||||
print_tag(const struct tag *tag)
|
|
||||||
{
|
|
||||||
if (tag->time >= 0)
|
|
||||||
g_print("time=%d\n", tag->time);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < tag->num_items; ++i)
|
static void
|
||||||
g_print("%s=%s\n",
|
print_duration(unsigned seconds, G_GNUC_UNUSED void *ctx)
|
||||||
tag_item_names[tag->items[i]->type],
|
{
|
||||||
tag->items[i]->value);
|
g_print("duration=%d\n", seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_tag(enum tag_type type, const char *value, G_GNUC_UNUSED void *ctx)
|
||||||
|
{
|
||||||
|
g_print("%s=%s\n", tag_item_names[type], value);
|
||||||
|
empty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct tag_handler print_handler = {
|
||||||
|
.duration = print_duration,
|
||||||
|
.tag = print_tag,
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
const char *decoder_name, *path;
|
const char *decoder_name, *path;
|
||||||
const struct decoder_plugin *plugin;
|
const struct decoder_plugin *plugin;
|
||||||
struct tag *tag;
|
|
||||||
bool empty;
|
|
||||||
|
|
||||||
#ifdef HAVE_LOCALE_H
|
#ifdef HAVE_LOCALE_H
|
||||||
/* initialize locale */
|
/* initialize locale */
|
||||||
@ -175,8 +181,6 @@ int main(int argc, char **argv)
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag_pool_init();
|
|
||||||
|
|
||||||
if (!input_stream_global_init(&error)) {
|
if (!input_stream_global_init(&error)) {
|
||||||
g_warning("%s", error->message);
|
g_warning("%s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
@ -191,8 +195,9 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = decoder_plugin_tag_dup(plugin, path);
|
bool success = decoder_plugin_scan_file(plugin, path,
|
||||||
if (tag == NULL && plugin->stream_tag != NULL) {
|
&print_handler, NULL);
|
||||||
|
if (!success && plugin->scan_stream != NULL) {
|
||||||
GMutex *mutex = g_mutex_new();
|
GMutex *mutex = g_mutex_new();
|
||||||
GCond *cond = g_cond_new();
|
GCond *cond = g_cond_new();
|
||||||
|
|
||||||
@ -206,7 +211,8 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = decoder_plugin_stream_tag(plugin, is);
|
success = decoder_plugin_scan_stream(plugin, is,
|
||||||
|
&print_handler, NULL);
|
||||||
input_stream_close(is);
|
input_stream_close(is);
|
||||||
|
|
||||||
g_cond_free(cond);
|
g_cond_free(cond);
|
||||||
@ -217,27 +223,16 @@ int main(int argc, char **argv)
|
|||||||
input_stream_global_finish();
|
input_stream_global_finish();
|
||||||
io_thread_deinit();
|
io_thread_deinit();
|
||||||
|
|
||||||
if (tag == NULL) {
|
if (!success) {
|
||||||
g_printerr("Failed to read tags\n");
|
g_printerr("Failed to read tags\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_tag(tag);
|
|
||||||
|
|
||||||
empty = tag_is_empty(tag);
|
|
||||||
tag_free(tag);
|
|
||||||
|
|
||||||
if (empty) {
|
if (empty) {
|
||||||
tag = tag_ape_load(path);
|
tag_ape_scan2(path, &print_handler, NULL);
|
||||||
if (tag == NULL)
|
if (empty)
|
||||||
tag = tag_id3_load(path);
|
tag_id3_scan(path, &print_handler, NULL);
|
||||||
if (tag != NULL) {
|
|
||||||
print_tag(tag);
|
|
||||||
tag_free(tag);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tag_pool_deinit();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user