tag/Handler: convert to class with virtual methods
This commit is contained in:
parent
09d4176210
commit
3d3a1232b1
@ -25,7 +25,7 @@
|
||||
|
||||
bool
|
||||
tag_archive_scan(ArchiveFile &archive, const char *path_utf8,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
TagHandler &handler) noexcept
|
||||
try {
|
||||
Mutex mutex;
|
||||
|
||||
@ -33,7 +33,7 @@ try {
|
||||
if (!is)
|
||||
return false;
|
||||
|
||||
return tag_stream_scan(*is, handler, handler_ctx);
|
||||
return tag_stream_scan(*is, handler);
|
||||
} catch (const std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "check.h"
|
||||
|
||||
class ArchiveFile;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
class TagBuilder;
|
||||
|
||||
/**
|
||||
@ -36,7 +36,7 @@ class TagBuilder;
|
||||
*/
|
||||
bool
|
||||
tag_archive_scan(ArchiveFile &archive, const char *path_utf8,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept;
|
||||
TagHandler &handler) noexcept;
|
||||
|
||||
/**
|
||||
* Scan the tags of a song file inside an archive. Invokes matching
|
||||
|
@ -36,21 +36,20 @@ class TagFileScan {
|
||||
const Path path_fs;
|
||||
const char *const suffix;
|
||||
|
||||
const TagHandler &handler;
|
||||
void *handler_ctx;
|
||||
TagHandler &handler;
|
||||
|
||||
Mutex mutex;
|
||||
InputStreamPtr is;
|
||||
|
||||
public:
|
||||
TagFileScan(Path _path_fs, const char *_suffix,
|
||||
const TagHandler &_handler, void *_handler_ctx) noexcept
|
||||
TagHandler &_handler) noexcept
|
||||
:path_fs(_path_fs), suffix(_suffix),
|
||||
handler(_handler), handler_ctx(_handler_ctx) ,
|
||||
handler(_handler),
|
||||
is(nullptr) {}
|
||||
|
||||
bool ScanFile(const DecoderPlugin &plugin) noexcept {
|
||||
return plugin.ScanFile(path_fs, handler, handler_ctx);
|
||||
return plugin.ScanFile(path_fs, handler);
|
||||
}
|
||||
|
||||
bool ScanStream(const DecoderPlugin &plugin) noexcept {
|
||||
@ -72,7 +71,7 @@ public:
|
||||
}
|
||||
|
||||
/* now try the stream_tag() method */
|
||||
return plugin.ScanStream(*is, handler, handler_ctx);
|
||||
return plugin.ScanStream(*is, handler);
|
||||
}
|
||||
|
||||
bool Scan(const DecoderPlugin &plugin) noexcept {
|
||||
@ -82,8 +81,7 @@ public:
|
||||
};
|
||||
|
||||
bool
|
||||
tag_file_scan(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
tag_file_scan(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
assert(!path_fs.IsNull());
|
||||
|
||||
@ -95,7 +93,7 @@ tag_file_scan(Path path_fs,
|
||||
|
||||
const auto suffix_utf8 = Path::FromFS(suffix).ToUTF8();
|
||||
|
||||
TagFileScan tfs(path_fs, suffix_utf8.c_str(), handler, handler_ctx);
|
||||
TagFileScan tfs(path_fs, suffix_utf8.c_str(), handler);
|
||||
return decoder_plugins_try([&](const DecoderPlugin &plugin){
|
||||
return tfs.Scan(plugin);
|
||||
});
|
||||
@ -104,11 +102,13 @@ tag_file_scan(Path path_fs,
|
||||
bool
|
||||
tag_file_scan(Path path, TagBuilder &builder) noexcept
|
||||
{
|
||||
if (!tag_file_scan(path, full_tag_handler, &builder))
|
||||
FullTagHandler h(builder);
|
||||
|
||||
if (!tag_file_scan(path, h))
|
||||
return false;
|
||||
|
||||
if (builder.empty())
|
||||
ScanGenericTags(path, full_tag_handler, &builder);
|
||||
ScanGenericTags(path, h);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "check.h"
|
||||
|
||||
class Path;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
class TagBuilder;
|
||||
|
||||
/**
|
||||
@ -34,8 +34,7 @@ class TagBuilder;
|
||||
* found)
|
||||
*/
|
||||
bool
|
||||
tag_file_scan(Path path,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept;
|
||||
tag_file_scan(Path path, TagHandler &handler) noexcept;
|
||||
|
||||
/**
|
||||
* Scan the tags of a song file. Invokes matching decoder plugins,
|
||||
|
@ -46,7 +46,7 @@ CheckDecoderPlugin(const DecoderPlugin &plugin,
|
||||
}
|
||||
|
||||
bool
|
||||
tag_stream_scan(InputStream &is, const TagHandler &handler, void *ctx)
|
||||
tag_stream_scan(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
assert(is.IsReady());
|
||||
|
||||
@ -62,44 +62,46 @@ tag_stream_scan(InputStream &is, const TagHandler &handler, void *ctx)
|
||||
mime = (mime_base = GetMimeTypeBase(mime)).c_str();
|
||||
|
||||
return decoder_plugins_try([suffix, mime, &is,
|
||||
&handler, ctx](const DecoderPlugin &plugin){
|
||||
&handler](const DecoderPlugin &plugin){
|
||||
try {
|
||||
is.LockRewind();
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
return CheckDecoderPlugin(plugin, suffix, mime) &&
|
||||
plugin.ScanStream(is, handler, ctx);
|
||||
plugin.ScanStream(is, handler);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
tag_stream_scan(const char *uri, const TagHandler &handler, void *ctx)
|
||||
tag_stream_scan(const char *uri, TagHandler &handler) noexcept
|
||||
try {
|
||||
Mutex mutex;
|
||||
|
||||
auto is = InputStream::OpenReady(uri, mutex);
|
||||
return tag_stream_scan(*is, handler, ctx);
|
||||
return tag_stream_scan(*is, handler);
|
||||
} catch (const std::exception &e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
tag_stream_scan(InputStream &is, TagBuilder &builder)
|
||||
tag_stream_scan(InputStream &is, TagBuilder &builder) noexcept
|
||||
{
|
||||
assert(is.IsReady());
|
||||
|
||||
if (!tag_stream_scan(is, full_tag_handler, &builder))
|
||||
FullTagHandler h(builder);
|
||||
|
||||
if (!tag_stream_scan(is, h))
|
||||
return false;
|
||||
|
||||
if (builder.empty())
|
||||
ScanGenericTags(is, full_tag_handler, &builder);
|
||||
ScanGenericTags(is, h);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
tag_stream_scan(const char *uri, TagBuilder &builder)
|
||||
tag_stream_scan(const char *uri, TagBuilder &builder) noexcept
|
||||
try {
|
||||
Mutex mutex;
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "check.h"
|
||||
|
||||
class InputStream;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
class TagBuilder;
|
||||
|
||||
/**
|
||||
@ -34,10 +34,10 @@ class TagBuilder;
|
||||
* found)
|
||||
*/
|
||||
bool
|
||||
tag_stream_scan(InputStream &is, const TagHandler &handler, void *ctx);
|
||||
tag_stream_scan(InputStream &is, TagHandler &handler) noexcept;
|
||||
|
||||
bool
|
||||
tag_stream_scan(const char *uri, const TagHandler &handler, void *ctx);
|
||||
tag_stream_scan(const char *uri, TagHandler &handler) noexcept;
|
||||
|
||||
/**
|
||||
* Scan the tags of an #InputStream. Invokes matching decoder
|
||||
@ -48,9 +48,9 @@ tag_stream_scan(const char *uri, const TagHandler &handler, void *ctx);
|
||||
* found)
|
||||
*/
|
||||
bool
|
||||
tag_stream_scan(InputStream &is, TagBuilder &builder);
|
||||
tag_stream_scan(InputStream &is, TagBuilder &builder) noexcept;
|
||||
|
||||
bool
|
||||
tag_stream_scan(const char *uri, TagBuilder &builder);
|
||||
tag_stream_scan(const char *uri, TagBuilder &builder) noexcept;
|
||||
|
||||
#endif
|
||||
|
@ -137,25 +137,24 @@ IsValidValue(const char *p) noexcept
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
print_pair(const char *key, const char *value, void *ctx)
|
||||
{
|
||||
auto &r = *(Response *)ctx;
|
||||
class PrintCommentHandler final : public NullTagHandler {
|
||||
Response &response;
|
||||
|
||||
if (IsValidName(key) && IsValidValue(value))
|
||||
r.Format("%s: %s\n", key, value);
|
||||
}
|
||||
public:
|
||||
explicit PrintCommentHandler(Response &_response) noexcept
|
||||
:NullTagHandler(WANT_PAIR), response(_response) {}
|
||||
|
||||
static constexpr TagHandler print_comment_handler = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
print_pair,
|
||||
void OnPair(const char *key, const char *value) noexcept override {
|
||||
if (IsValidName(key) && IsValidValue(value))
|
||||
response.Format("%s: %s\n", key, value);
|
||||
}
|
||||
};
|
||||
|
||||
static CommandResult
|
||||
read_stream_comments(Response &r, const char *uri)
|
||||
{
|
||||
if (!tag_stream_scan(uri, print_comment_handler, &r)) {
|
||||
PrintCommentHandler h(r);
|
||||
if (!tag_stream_scan(uri, h)) {
|
||||
r.Error(ACK_ERROR_NO_EXIST, "Failed to load file");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
@ -167,12 +166,13 @@ read_stream_comments(Response &r, const char *uri)
|
||||
static CommandResult
|
||||
read_file_comments(Response &r, const Path path_fs)
|
||||
{
|
||||
if (!tag_file_scan(path_fs, print_comment_handler, &r)) {
|
||||
PrintCommentHandler h(r);
|
||||
if (!tag_file_scan(path_fs, h)) {
|
||||
r.Error(ACK_ERROR_NO_EXIST, "Failed to load file");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
||||
ScanGenericTags(path_fs, print_comment_handler, &r);
|
||||
ScanGenericTags(path_fs, h);
|
||||
|
||||
return CommandResult::OK;
|
||||
|
||||
|
@ -93,15 +93,6 @@ handle_kill(gcc_unused Client &client, gcc_unused Request request,
|
||||
return CommandResult::KILL;
|
||||
}
|
||||
|
||||
static void
|
||||
print_tag(TagType type, const char *value, void *ctx)
|
||||
{
|
||||
auto &r = *(Response *)ctx;
|
||||
|
||||
if (r.GetClient().tag_mask.Test(type))
|
||||
tag_print(r, type, value);
|
||||
}
|
||||
|
||||
CommandResult
|
||||
handle_listfiles(Client &client, Request args, Response &r)
|
||||
{
|
||||
@ -149,16 +140,24 @@ handle_listfiles(Client &client, Request args, Response &r)
|
||||
gcc_unreachable();
|
||||
}
|
||||
|
||||
static constexpr TagHandler print_tag_handler = {
|
||||
nullptr,
|
||||
print_tag,
|
||||
nullptr,
|
||||
class PrintTagHandler final : public NullTagHandler {
|
||||
Response &response;
|
||||
|
||||
public:
|
||||
explicit PrintTagHandler(Response &_response) noexcept
|
||||
:NullTagHandler(WANT_TAG), response(_response) {}
|
||||
|
||||
void OnTag(TagType type, const char *value) noexcept override {
|
||||
if (response.GetClient().tag_mask.Test(type))
|
||||
tag_print(response, type, value);
|
||||
}
|
||||
};
|
||||
|
||||
static CommandResult
|
||||
handle_lsinfo_absolute(Response &r, const char *uri)
|
||||
{
|
||||
if (!tag_stream_scan(uri, print_tag_handler, &r)) {
|
||||
PrintTagHandler h(r);
|
||||
if (!tag_stream_scan(uri, h)) {
|
||||
r.Error(ACK_ERROR_NO_EXIST, "No such file");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
struct ConfigBlock;
|
||||
class InputStream;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
class Path;
|
||||
class DecoderClient;
|
||||
class DetachedSong;
|
||||
@ -71,18 +71,14 @@ struct DecoderPlugin {
|
||||
*
|
||||
* @return false if the operation has failed
|
||||
*/
|
||||
bool (*scan_file)(Path path_fs,
|
||||
const TagHandler &handler,
|
||||
void *handler_ctx) noexcept;
|
||||
bool (*scan_file)(Path path_fs, TagHandler &handler) noexcept;
|
||||
|
||||
/**
|
||||
* Scan metadata of a file.
|
||||
*
|
||||
* @return false if the operation has failed
|
||||
*/
|
||||
bool (*scan_stream)(InputStream &is,
|
||||
const TagHandler &handler,
|
||||
void *handler_ctx) noexcept;
|
||||
bool (*scan_stream)(InputStream &is, TagHandler &handler) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Return a "virtual" filename for subtracks in
|
||||
@ -139,20 +135,18 @@ struct DecoderPlugin {
|
||||
* Read the tag of a file.
|
||||
*/
|
||||
template<typename P>
|
||||
bool ScanFile(P path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) const noexcept {
|
||||
bool ScanFile(P path_fs, TagHandler &handler) const noexcept {
|
||||
return scan_file != nullptr
|
||||
? scan_file(path_fs, handler, handler_ctx)
|
||||
? scan_file(path_fs, handler)
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the tag of a stream.
|
||||
*/
|
||||
bool ScanStream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) const noexcept {
|
||||
bool ScanStream(InputStream &is, TagHandler &handler) const noexcept {
|
||||
return scan_stream != nullptr
|
||||
? scan_stream(is, handler, handler_ctx)
|
||||
? scan_stream(is, handler)
|
||||
: false;
|
||||
}
|
||||
|
||||
|
@ -83,16 +83,14 @@ adplug_file_decode(DecoderClient &client, Path path_fs)
|
||||
|
||||
static void
|
||||
adplug_scan_tag(TagType type, const std::string &value,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
if (!value.empty())
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
type, value.c_str());
|
||||
handler.OnTag(type, value.c_str());
|
||||
}
|
||||
|
||||
static bool
|
||||
adplug_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
adplug_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
CEmuopl opl(sample_rate, true, true);
|
||||
opl.init();
|
||||
@ -101,16 +99,15 @@ adplug_scan_file(Path path_fs,
|
||||
if (player == nullptr)
|
||||
return false;
|
||||
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
SongTime::FromMS(player->songlength()));
|
||||
handler.OnDuration(SongTime::FromMS(player->songlength()));
|
||||
|
||||
if (handler.tag != nullptr) {
|
||||
if (handler.WantTag()) {
|
||||
adplug_scan_tag(TAG_TITLE, player->gettitle(),
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
adplug_scan_tag(TAG_ARTIST, player->getauthor(),
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
adplug_scan_tag(TAG_COMMENT, player->getdesc(),
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
}
|
||||
|
||||
delete player;
|
||||
|
@ -256,14 +256,13 @@ audiofile_get_duration(InputStream &is) noexcept
|
||||
}
|
||||
|
||||
static bool
|
||||
audiofile_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
audiofile_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
const auto duration = audiofile_get_duration(is);
|
||||
if (duration.IsNegative())
|
||||
return false;
|
||||
|
||||
tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
|
||||
handler.OnDuration(SongTime(duration));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -115,9 +115,8 @@ dsdlib_valid_freq(uint32_t samplefreq) noexcept
|
||||
|
||||
#ifdef ENABLE_ID3TAG
|
||||
void
|
||||
dsdlib_tag_id3(InputStream &is,
|
||||
const TagHandler &handler,
|
||||
void *handler_ctx, offset_type tagoffset)
|
||||
dsdlib_tag_id3(InputStream &is, TagHandler &handler,
|
||||
offset_type tagoffset)
|
||||
{
|
||||
if (tagoffset == 0 || !is.KnownSize())
|
||||
return;
|
||||
@ -150,7 +149,7 @@ dsdlib_tag_id3(InputStream &is,
|
||||
if (id3_tag == nullptr)
|
||||
return;
|
||||
|
||||
scan_id3_tag(id3_tag, handler, handler_ctx);
|
||||
scan_id3_tag(id3_tag, handler);
|
||||
|
||||
id3_tag_delete(id3_tag);
|
||||
return;
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
class DecoderClient;
|
||||
class InputStream;
|
||||
|
||||
@ -79,8 +79,7 @@ dsdlib_valid_freq(uint32_t samplefreq) noexcept;
|
||||
* DSF and DSDIFF files are imported
|
||||
*/
|
||||
void
|
||||
dsdlib_tag_id3(InputStream &is,
|
||||
const TagHandler &handler,
|
||||
void *handler_ctx, offset_type tagoffset);
|
||||
dsdlib_tag_id3(InputStream &is, TagHandler &handler,
|
||||
offset_type tagoffset);
|
||||
|
||||
#endif
|
||||
|
@ -186,8 +186,8 @@ dsdiff_read_prop(DecoderClient *client, InputStream &is,
|
||||
|
||||
static void
|
||||
dsdiff_handle_native_tag(InputStream &is,
|
||||
const TagHandler &handler,
|
||||
void *handler_ctx, offset_type tagoffset,
|
||||
TagHandler &handler,
|
||||
offset_type tagoffset,
|
||||
TagType type)
|
||||
{
|
||||
if (!dsdlib_skip_to(nullptr, is, tagoffset))
|
||||
@ -212,7 +212,7 @@ dsdiff_handle_native_tag(InputStream &is,
|
||||
return;
|
||||
|
||||
string[length] = '\0';
|
||||
tag_handler_invoke_tag(handler, handler_ctx, type, label);
|
||||
handler.OnTag(type, label);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -228,8 +228,7 @@ static bool
|
||||
dsdiff_read_metadata_extra(DecoderClient *client, InputStream &is,
|
||||
DsdiffMetaData *metadata,
|
||||
DsdiffChunkHeader *chunk_header,
|
||||
const TagHandler &handler,
|
||||
void *handler_ctx)
|
||||
TagHandler &handler)
|
||||
{
|
||||
|
||||
/* skip from DSD data to next chunk header */
|
||||
@ -286,17 +285,17 @@ dsdiff_read_metadata_extra(DecoderClient *client, InputStream &is,
|
||||
if (id3_offset != 0) {
|
||||
/* a ID3 tag has preference over the other tags, do not process
|
||||
other tags if we have one */
|
||||
dsdlib_tag_id3(is, handler, handler_ctx, id3_offset);
|
||||
dsdlib_tag_id3(is, handler, id3_offset);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (artist_offset != 0)
|
||||
dsdiff_handle_native_tag(is, handler, handler_ctx,
|
||||
dsdiff_handle_native_tag(is, handler,
|
||||
artist_offset, TAG_ARTIST);
|
||||
|
||||
if (title_offset != 0)
|
||||
dsdiff_handle_native_tag(is, handler, handler_ctx,
|
||||
dsdiff_handle_native_tag(is, handler,
|
||||
title_offset, TAG_TITLE);
|
||||
return true;
|
||||
}
|
||||
@ -449,9 +448,7 @@ dsdiff_stream_decode(DecoderClient &client, InputStream &is)
|
||||
}
|
||||
|
||||
static bool
|
||||
dsdiff_scan_stream(InputStream &is,
|
||||
gcc_unused const TagHandler &handler,
|
||||
gcc_unused void *handler_ctx) noexcept
|
||||
dsdiff_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
DsdiffMetaData metadata;
|
||||
DsdiffChunkHeader chunk_header;
|
||||
@ -469,11 +466,11 @@ dsdiff_scan_stream(InputStream &is,
|
||||
uint64_t n_frames = metadata.chunk_size / metadata.channels;
|
||||
auto songtime = SongTime::FromScale<uint64_t>(n_frames,
|
||||
sample_rate);
|
||||
tag_handler_invoke_duration(handler, handler_ctx, songtime);
|
||||
handler.OnDuration(songtime);
|
||||
|
||||
/* Read additional metadata and created tags if available */
|
||||
dsdiff_read_metadata_extra(nullptr, is, &metadata, &chunk_header,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -325,9 +325,7 @@ dsf_stream_decode(DecoderClient &client, InputStream &is)
|
||||
}
|
||||
|
||||
static bool
|
||||
dsf_scan_stream(InputStream &is,
|
||||
gcc_unused const TagHandler &handler,
|
||||
gcc_unused void *handler_ctx) noexcept
|
||||
dsf_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
/* check DSF metadata */
|
||||
DsfMetaData metadata;
|
||||
@ -342,11 +340,11 @@ dsf_scan_stream(InputStream &is,
|
||||
const auto n_blocks = metadata.n_blocks;
|
||||
auto songtime = SongTime::FromScale<uint64_t>(n_blocks * DSF_BLOCK_SIZE,
|
||||
sample_rate);
|
||||
tag_handler_invoke_duration(handler, handler_ctx, songtime);
|
||||
handler.OnDuration(songtime);
|
||||
|
||||
#ifdef ENABLE_ID3TAG
|
||||
/* Add available tags from the ID3 tag */
|
||||
dsdlib_tag_id3(is, handler, handler_ctx, metadata.id3_offset);
|
||||
dsdlib_tag_id3(is, handler, metadata.id3_offset);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
@ -414,16 +414,14 @@ faad_stream_decode(DecoderClient &client, InputStream &is)
|
||||
}
|
||||
|
||||
static bool
|
||||
faad_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
faad_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
auto result = faad_get_file_time(is);
|
||||
if (!result.first)
|
||||
return false;
|
||||
|
||||
if (!result.second.IsNegative())
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
SongTime(result.second));
|
||||
handler.OnDuration(SongTime(result.second));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -572,21 +572,20 @@ FfmpegParseMetaData(DecoderClient &client,
|
||||
}
|
||||
|
||||
static void
|
||||
FfmpegScanMetadata(const AVStream &stream,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
FfmpegScanMetadata(const AVStream &stream, TagHandler &handler) noexcept
|
||||
{
|
||||
FfmpegScanDictionary(stream.metadata, handler, handler_ctx);
|
||||
FfmpegScanDictionary(stream.metadata, handler);
|
||||
}
|
||||
|
||||
static void
|
||||
FfmpegScanMetadata(const AVFormatContext &format_context, int audio_stream,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
assert(audio_stream >= 0);
|
||||
|
||||
FfmpegScanDictionary(format_context.metadata, handler, handler_ctx);
|
||||
FfmpegScanDictionary(format_context.metadata, handler);
|
||||
FfmpegScanMetadata(*format_context.streams[audio_stream],
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
}
|
||||
|
||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(56, 1, 0)
|
||||
@ -595,8 +594,8 @@ static void
|
||||
FfmpegScanTag(const AVFormatContext &format_context, int audio_stream,
|
||||
TagBuilder &tag)
|
||||
{
|
||||
FfmpegScanMetadata(format_context, audio_stream,
|
||||
full_tag_handler, &tag);
|
||||
FullTagHandler h(tag);
|
||||
FfmpegScanMetadata(format_context, audio_stream, h);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -828,7 +827,7 @@ ffmpeg_decode(DecoderClient &client, InputStream &input)
|
||||
|
||||
static bool
|
||||
FfmpegScanStream(AVFormatContext &format_context,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
const int find_result =
|
||||
avformat_find_stream_info(&format_context, nullptr);
|
||||
@ -841,22 +840,19 @@ FfmpegScanStream(AVFormatContext &format_context,
|
||||
|
||||
const AVStream &stream = *format_context.streams[audio_stream];
|
||||
if (stream.duration != (int64_t)AV_NOPTS_VALUE)
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
FromFfmpegTime(stream.duration,
|
||||
stream.time_base));
|
||||
handler.OnDuration(FromFfmpegTime(stream.duration,
|
||||
stream.time_base));
|
||||
else if (format_context.duration != (int64_t)AV_NOPTS_VALUE)
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
FromFfmpegTime(format_context.duration,
|
||||
AV_TIME_BASE_Q));
|
||||
handler.OnDuration(FromFfmpegTime(format_context.duration,
|
||||
AV_TIME_BASE_Q));
|
||||
|
||||
FfmpegScanMetadata(format_context, audio_stream, handler, handler_ctx);
|
||||
FfmpegScanMetadata(format_context, audio_stream, handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ffmpeg_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
ffmpeg_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
AVInputFormat *input_format = ffmpeg_probe(nullptr, is);
|
||||
if (input_format == nullptr)
|
||||
@ -877,7 +873,7 @@ ffmpeg_scan_stream(InputStream &is,
|
||||
avformat_close_input(&f);
|
||||
};
|
||||
|
||||
return FfmpegScanStream(*f, handler, handler_ctx);
|
||||
return FfmpegScanStream(*f, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,46 +43,42 @@ static constexpr struct tag_table ffmpeg_tags[] = {
|
||||
static void
|
||||
FfmpegScanTag(TagType type,
|
||||
AVDictionary *m, const char *name,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
AVDictionaryEntry *mt = nullptr;
|
||||
|
||||
while ((mt = av_dict_get(m, name, mt, 0)) != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
type, mt->value);
|
||||
handler.OnTag(type, mt->value);
|
||||
}
|
||||
|
||||
static void
|
||||
FfmpegScanPairs(AVDictionary *dict,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
FfmpegScanPairs(AVDictionary *dict, TagHandler &handler) noexcept
|
||||
{
|
||||
AVDictionaryEntry *i = nullptr;
|
||||
|
||||
while ((i = av_dict_get(dict, "", i, AV_DICT_IGNORE_SUFFIX)) != nullptr)
|
||||
tag_handler_invoke_pair(handler, handler_ctx,
|
||||
i->key, i->value);
|
||||
handler.OnPair(i->key, i->value);
|
||||
}
|
||||
|
||||
void
|
||||
FfmpegScanDictionary(AVDictionary *dict,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
FfmpegScanDictionary(AVDictionary *dict, TagHandler &handler) noexcept
|
||||
{
|
||||
if (handler.tag != nullptr) {
|
||||
if (handler.WantTag()) {
|
||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
||||
FfmpegScanTag(TagType(i), dict, tag_item_names[i],
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
|
||||
for (const struct tag_table *i = ffmpeg_tags;
|
||||
i->name != nullptr; ++i)
|
||||
FfmpegScanTag(i->type, dict, i->name,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
|
||||
for (const struct tag_table *i = musicbrainz_txxx_tags;
|
||||
i->name != nullptr; ++i)
|
||||
FfmpegScanTag(i->type, dict, i->name,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
}
|
||||
|
||||
if (handler.pair != nullptr)
|
||||
FfmpegScanPairs(dict, handler, handler_ctx);
|
||||
if (handler.WantPair())
|
||||
FfmpegScanPairs(dict, handler);
|
||||
}
|
||||
|
@ -21,10 +21,9 @@
|
||||
#define MPD_FFMPEG_METADATA_HXX
|
||||
|
||||
struct AVDictionary;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
|
||||
void
|
||||
FfmpegScanDictionary(AVDictionary *dict,
|
||||
const TagHandler &handler, void *handler_ctx);
|
||||
FfmpegScanDictionary(AVDictionary *dict, TagHandler &handler) noexcept;
|
||||
|
||||
#endif
|
||||
|
@ -69,8 +69,7 @@ flac_write_cb(const FLAC__StreamDecoder *dec, const FLAC__Frame *frame,
|
||||
}
|
||||
|
||||
static bool
|
||||
flac_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
flac_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
FlacMetadataChain chain;
|
||||
if (!chain.Read(NarrowPath(path_fs))) {
|
||||
@ -80,13 +79,12 @@ flac_scan_file(Path path_fs,
|
||||
return false;
|
||||
}
|
||||
|
||||
chain.Scan(handler, handler_ctx);
|
||||
chain.Scan(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
flac_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
flac_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
FlacMetadataChain chain;
|
||||
if (!chain.Read(is)) {
|
||||
@ -96,7 +94,7 @@ flac_scan_stream(InputStream &is,
|
||||
return false;
|
||||
}
|
||||
|
||||
chain.Scan(handler, handler_ctx);
|
||||
chain.Scan(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -315,8 +313,7 @@ oggflac_init(gcc_unused const ConfigBlock &block)
|
||||
}
|
||||
|
||||
static bool
|
||||
oggflac_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
oggflac_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
FlacMetadataChain chain;
|
||||
if (!chain.ReadOgg(NarrowPath(path_fs))) {
|
||||
@ -326,13 +323,12 @@ oggflac_scan_file(Path path_fs,
|
||||
return false;
|
||||
}
|
||||
|
||||
chain.Scan(handler, handler_ctx);
|
||||
chain.Scan(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
oggflac_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
oggflac_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
FlacMetadataChain chain;
|
||||
if (!chain.ReadOgg(is)) {
|
||||
@ -342,7 +338,7 @@ oggflac_scan_stream(InputStream &is,
|
||||
return false;
|
||||
}
|
||||
|
||||
chain.Scan(handler, handler_ctx);
|
||||
chain.Scan(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -79,11 +79,11 @@ flac_comment_value(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
||||
static bool
|
||||
flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
||||
const char *name, TagType tag_type,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
const char *value = flac_comment_value(entry, name);
|
||||
if (value != nullptr) {
|
||||
tag_handler_invoke_tag(handler, handler_ctx, tag_type, value);
|
||||
handler.OnTag(tag_type, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -92,36 +92,33 @@ flac_copy_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
||||
|
||||
static void
|
||||
flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
if (handler.pair != nullptr) {
|
||||
if (handler.WantPair()) {
|
||||
const char *comment = (const char *)entry->entry;
|
||||
const DivideString split(comment, '=');
|
||||
if (split.IsDefined() && !split.empty())
|
||||
tag_handler_invoke_pair(handler, handler_ctx,
|
||||
split.GetFirst(),
|
||||
split.GetSecond());
|
||||
handler.OnPair(split.GetFirst(), split.GetSecond());
|
||||
}
|
||||
|
||||
for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
|
||||
if (flac_copy_comment(entry, i->name, i->type,
|
||||
handler, handler_ctx))
|
||||
if (flac_copy_comment(entry, i->name, i->type, handler))
|
||||
return;
|
||||
|
||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
||||
if (flac_copy_comment(entry,
|
||||
tag_item_names[i], (TagType)i,
|
||||
handler, handler_ctx))
|
||||
handler))
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
flac_scan_comments(const FLAC__StreamMetadata_VorbisComment *comment,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
for (unsigned i = 0; i < comment->num_comments; ++i)
|
||||
flac_scan_comment(&comment->comments[i],
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
@ -136,18 +133,17 @@ flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info) noexcept
|
||||
|
||||
void
|
||||
flac_scan_metadata(const FLAC__StreamMetadata *block,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
switch (block->type) {
|
||||
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
|
||||
flac_scan_comments(&block->data.vorbis_comment,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
break;
|
||||
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
if (block->data.stream_info.sample_rate > 0)
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
flac_duration(&block->data.stream_info));
|
||||
handler.OnDuration(flac_duration(&block->data.stream_info));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -159,12 +155,13 @@ Tag
|
||||
flac_vorbis_comments_to_tag(const FLAC__StreamMetadata_VorbisComment *comment)
|
||||
{
|
||||
TagBuilder tag_builder;
|
||||
flac_scan_comments(comment, add_tag_handler, &tag_builder);
|
||||
AddTagHandler h(tag_builder);
|
||||
flac_scan_comments(comment, h);
|
||||
return tag_builder.Commit();
|
||||
}
|
||||
|
||||
void
|
||||
FlacMetadataChain::Scan(const TagHandler &handler, void *handler_ctx)
|
||||
FlacMetadataChain::Scan(TagHandler &handler) noexcept
|
||||
{
|
||||
FLACMetadataIterator iterator(*this);
|
||||
|
||||
@ -173,6 +170,6 @@ FlacMetadataChain::Scan(const TagHandler &handler, void *handler_ctx)
|
||||
if (block == nullptr)
|
||||
break;
|
||||
|
||||
flac_scan_metadata(block, handler, handler_ctx);
|
||||
flac_scan_metadata(block, handler);
|
||||
} while (iterator.Next());
|
||||
}
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include <FLAC/metadata.h>
|
||||
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
class MixRampInfo;
|
||||
|
||||
class FlacMetadataChain {
|
||||
@ -82,7 +82,7 @@ public:
|
||||
return FLAC__Metadata_ChainStatusString[GetStatus()];
|
||||
}
|
||||
|
||||
void Scan(const TagHandler &handler, void *handler_ctx);
|
||||
void Scan(TagHandler &handler) noexcept;
|
||||
};
|
||||
|
||||
class FLACMetadataIterator {
|
||||
@ -126,6 +126,6 @@ flac_vorbis_comments_to_tag(const FLAC__StreamMetadata_VorbisComment *comment);
|
||||
|
||||
void
|
||||
flac_scan_metadata(const FLAC__StreamMetadata *block,
|
||||
const TagHandler &handler, void *handler_ctx);
|
||||
TagHandler &handler) noexcept;
|
||||
|
||||
#endif
|
||||
|
@ -193,8 +193,7 @@ fluidsynth_file_decode(DecoderClient &client, Path path_fs)
|
||||
|
||||
static bool
|
||||
fluidsynth_scan_file(Path path_fs,
|
||||
gcc_unused const TagHandler &handler,
|
||||
gcc_unused void *handler_ctx) noexcept
|
||||
gcc_unused TagHandler &handler) noexcept
|
||||
{
|
||||
return fluid_is_midifile(path_fs.c_str());
|
||||
}
|
||||
|
@ -215,15 +215,13 @@ gme_file_decode(DecoderClient &client, Path path_fs)
|
||||
|
||||
static void
|
||||
ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
if (info.play_length > 0)
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
SongTime::FromMS(info.play_length));
|
||||
handler.OnDuration(SongTime::FromMS(info.play_length));
|
||||
|
||||
if (track_count > 1)
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK,
|
||||
StringFormat<16>("%u", song_num + 1));
|
||||
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", song_num + 1));
|
||||
|
||||
if (info.song != nullptr) {
|
||||
if (track_count > 1) {
|
||||
@ -232,33 +230,26 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
|
||||
StringFormat<1024>("%s (%u/%d)",
|
||||
info.song, song_num + 1,
|
||||
track_count);
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_TITLE, tag_title);
|
||||
handler.OnTag(TAG_TITLE, tag_title);
|
||||
} else
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_TITLE, info.song);
|
||||
handler.OnTag(TAG_TITLE, info.song);
|
||||
}
|
||||
|
||||
if (info.author != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_ARTIST, info.author);
|
||||
handler.OnTag(TAG_ARTIST, info.author);
|
||||
|
||||
if (info.game != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_ALBUM, info.game);
|
||||
handler.OnTag(TAG_ALBUM, info.game);
|
||||
|
||||
if (info.comment != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_COMMENT, info.comment);
|
||||
handler.OnTag(TAG_COMMENT, info.comment);
|
||||
|
||||
if (info.copyright != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_DATE, info.copyright);
|
||||
handler.OnTag(TAG_DATE, info.copyright);
|
||||
}
|
||||
|
||||
static bool
|
||||
ScanMusicEmu(Music_Emu *emu, unsigned song_num,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
ScanMusicEmu(Music_Emu *emu, unsigned song_num, TagHandler &handler) noexcept
|
||||
{
|
||||
gme_info_t *ti;
|
||||
const char *gme_err = gme_track_info(emu, &ti, song_num);
|
||||
@ -271,14 +262,12 @@ ScanMusicEmu(Music_Emu *emu, unsigned song_num,
|
||||
|
||||
AtScopeExit(ti) { gme_free_info(ti); };
|
||||
|
||||
ScanGmeInfo(*ti, song_num, gme_track_count(emu),
|
||||
handler, handler_ctx);
|
||||
ScanGmeInfo(*ti, song_num, gme_track_count(emu), handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
gme_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
gme_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
const auto container = ParseContainerPath(path_fs);
|
||||
|
||||
@ -289,7 +278,7 @@ gme_scan_file(Path path_fs,
|
||||
|
||||
AtScopeExit(emu) { gme_delete(emu); };
|
||||
|
||||
return ScanMusicEmu(emu, container.track, handler, handler_ctx);
|
||||
return ScanMusicEmu(emu, container.track, handler);
|
||||
}
|
||||
|
||||
static std::forward_list<DetachedSong>
|
||||
@ -316,8 +305,8 @@ gme_container_scan(Path path_fs)
|
||||
|
||||
auto tail = list.before_begin();
|
||||
for (unsigned i = 0; i < num_songs; ++i) {
|
||||
ScanMusicEmu(emu, i,
|
||||
add_tag_handler, &tag_builder);
|
||||
AddTagHandler h(tag_builder);
|
||||
ScanMusicEmu(emu, i, h);
|
||||
|
||||
const auto track_name =
|
||||
StringFormat<64>(SUBTUNE_PREFIX "%03u.%s", i+1,
|
||||
|
@ -1062,16 +1062,14 @@ mp3_decode(DecoderClient &client, InputStream &input_stream)
|
||||
}
|
||||
|
||||
static bool
|
||||
mad_decoder_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
mad_decoder_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
const auto result = mad_decoder_total_file_time(is);
|
||||
if (!result.first)
|
||||
return false;
|
||||
|
||||
if (!result.second.IsNegative())
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
SongTime(result.second));
|
||||
handler.OnDuration(SongTime(result.second));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -185,8 +185,7 @@ mikmod_decoder_file_decode(DecoderClient &client, Path path_fs)
|
||||
}
|
||||
|
||||
static bool
|
||||
mikmod_decoder_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
mikmod_decoder_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
/* deconstify the path because libmikmod wants a non-const
|
||||
string pointer */
|
||||
@ -204,8 +203,7 @@ mikmod_decoder_scan_file(Path path_fs,
|
||||
|
||||
char *title = Player_LoadTitle(path2);
|
||||
if (title != nullptr) {
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_TITLE, title);
|
||||
handler.OnTag(TAG_TITLE, title);
|
||||
#if (LIBMIKMOD_VERSION >= 0x030200)
|
||||
MikMod_free(title);
|
||||
#else
|
||||
|
@ -175,20 +175,17 @@ mod_decode(DecoderClient &client, InputStream &is)
|
||||
}
|
||||
|
||||
static bool
|
||||
modplug_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
modplug_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
ModPlugFile *f = LoadModPlugFile(nullptr, is);
|
||||
if (f == nullptr)
|
||||
return false;
|
||||
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
SongTime::FromMS(ModPlug_GetLength(f)));
|
||||
handler.OnDuration(SongTime::FromMS(ModPlug_GetLength(f)));
|
||||
|
||||
const char *title = ModPlug_GetName(f);
|
||||
if (title != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_TITLE, title);
|
||||
handler.OnTag(TAG_TITLE, title);
|
||||
|
||||
ModPlug_Unload(f);
|
||||
|
||||
|
@ -257,14 +257,13 @@ mpcdec_get_file_duration(InputStream &is)
|
||||
}
|
||||
|
||||
static bool
|
||||
mpcdec_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
mpcdec_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
const auto duration = mpcdec_get_file_duration(is);
|
||||
if (duration.IsNegative())
|
||||
return false;
|
||||
|
||||
tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
|
||||
handler.OnDuration(SongTime(duration));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -279,8 +279,7 @@ mpd_mpg123_file_decode(DecoderClient &client, Path path_fs)
|
||||
}
|
||||
|
||||
static bool
|
||||
mpd_mpg123_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
mpd_mpg123_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
int error;
|
||||
mpg123_handle *const handle = mpg123_new(nullptr, &error);
|
||||
@ -316,7 +315,7 @@ mpd_mpg123_scan_file(Path path_fs,
|
||||
SongTime::FromScale<uint64_t>(num_samples,
|
||||
audio_format.sample_rate);
|
||||
|
||||
tag_handler_invoke_duration(handler, handler_ctx, duration);
|
||||
handler.OnDuration(duration);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -207,10 +207,9 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
|
||||
rgi.Clear();
|
||||
|
||||
TagBuilder tag_builder;
|
||||
AddTagHandler h(tag_builder);
|
||||
|
||||
if (ScanOpusTags(packet.packet, packet.bytes,
|
||||
&rgi,
|
||||
add_tag_handler, &tag_builder) &&
|
||||
if (ScanOpusTags(packet.packet, packet.bytes, &rgi, h) &&
|
||||
!tag_builder.empty()) {
|
||||
client.SubmitReplayGain(&rgi);
|
||||
|
||||
@ -314,7 +313,7 @@ ReadAndParseOpusHead(OggSyncState &sync, OggStreamState &stream,
|
||||
|
||||
static bool
|
||||
ReadAndVisitOpusTags(OggSyncState &sync, OggStreamState &stream,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler)
|
||||
{
|
||||
ogg_packet packet;
|
||||
|
||||
@ -322,12 +321,12 @@ ReadAndVisitOpusTags(OggSyncState &sync, OggStreamState &stream,
|
||||
IsOpusTags(packet) &&
|
||||
ScanOpusTags(packet.packet, packet.bytes,
|
||||
nullptr,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
}
|
||||
|
||||
static void
|
||||
VisitOpusDuration(InputStream &is, OggSyncState &sync, OggStreamState &stream,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler)
|
||||
{
|
||||
ogg_packet packet;
|
||||
|
||||
@ -335,13 +334,12 @@ VisitOpusDuration(InputStream &is, OggSyncState &sync, OggStreamState &stream,
|
||||
const auto duration =
|
||||
SongTime::FromScale<uint64_t>(packet.granulepos,
|
||||
opus_sample_rate);
|
||||
tag_handler_invoke_duration(handler, handler_ctx, duration);
|
||||
handler.OnDuration(duration);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
mpd_opus_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
mpd_opus_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
InputStreamReader reader(is);
|
||||
OggSyncState oy(reader);
|
||||
@ -354,10 +352,10 @@ mpd_opus_scan_stream(InputStream &is,
|
||||
|
||||
unsigned channels;
|
||||
if (!ReadAndParseOpusHead(oy, os, channels) ||
|
||||
!ReadAndVisitOpusTags(oy, os, handler, handler_ctx))
|
||||
!ReadAndVisitOpusTags(oy, os, handler))
|
||||
return false;
|
||||
|
||||
VisitOpusDuration(is, oy, os, handler, handler_ctx);
|
||||
VisitOpusDuration(is, oy, os, handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ ParseOpusTagName(const char *name) noexcept
|
||||
static void
|
||||
ScanOneOpusTag(const char *name, const char *value,
|
||||
ReplayGainInfo *rgi,
|
||||
const TagHandler &handler, void *ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
if (rgi != nullptr && strcmp(name, "R128_TRACK_GAIN") == 0) {
|
||||
/* R128_TRACK_GAIN is a Q7.8 fixed point number in
|
||||
@ -63,25 +63,25 @@ ScanOneOpusTag(const char *name, const char *value,
|
||||
rgi->album.gain = double(l) / 256.;
|
||||
}
|
||||
|
||||
tag_handler_invoke_pair(handler, ctx, name, value);
|
||||
handler.OnPair(name, value);
|
||||
|
||||
if (handler.tag != nullptr) {
|
||||
if (handler.WantTag()) {
|
||||
TagType t = ParseOpusTagName(name);
|
||||
if (t != TAG_NUM_OF_ITEM_TYPES)
|
||||
tag_handler_invoke_tag(handler, ctx, t, value);
|
||||
handler.OnTag(t, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ScanOpusTags(const void *data, size_t size,
|
||||
ReplayGainInfo *rgi,
|
||||
const TagHandler &handler, void *ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
OpusReader r(data, size);
|
||||
if (!r.Expect("OpusTags", 8))
|
||||
return false;
|
||||
|
||||
if (handler.pair == nullptr && handler.tag == nullptr)
|
||||
if (!handler.WantPair() && !handler.WantTag())
|
||||
return true;
|
||||
|
||||
if (!r.SkipString())
|
||||
@ -100,7 +100,7 @@ ScanOpusTags(const void *data, size_t size,
|
||||
if (eq != nullptr && eq > p) {
|
||||
*eq = 0;
|
||||
|
||||
ScanOneOpusTag(p, eq + 1, rgi, handler, ctx);
|
||||
ScanOneOpusTag(p, eq + 1, rgi, handler);
|
||||
}
|
||||
|
||||
delete[] p;
|
||||
|
@ -25,11 +25,11 @@
|
||||
#include <stddef.h>
|
||||
|
||||
struct ReplayGainInfo;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
|
||||
bool
|
||||
ScanOpusTags(const void *data, size_t size,
|
||||
ReplayGainInfo *rgi,
|
||||
const TagHandler &handler, void *ctx);
|
||||
TagHandler &handler) noexcept;
|
||||
|
||||
#endif
|
||||
|
@ -405,7 +405,7 @@ GetInfoString(const SidTuneInfo &info, unsigned i) noexcept
|
||||
|
||||
static void
|
||||
ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
/* title */
|
||||
const char *title = GetInfoString(info, 0);
|
||||
@ -416,31 +416,26 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
|
||||
const auto tag_title =
|
||||
StringFormat<1024>("%s (%u/%u)",
|
||||
title, track, n_tracks);
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_TITLE, tag_title);
|
||||
handler.OnTag(TAG_TITLE, tag_title);
|
||||
} else
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, title);
|
||||
handler.OnTag(TAG_TITLE, title);
|
||||
|
||||
/* artist */
|
||||
const char *artist = GetInfoString(info, 1);
|
||||
if (artist != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_ARTIST,
|
||||
artist);
|
||||
handler.OnTag(TAG_ARTIST, artist);
|
||||
|
||||
/* date */
|
||||
const char *date = GetInfoString(info, 2);
|
||||
if (date != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_DATE,
|
||||
date);
|
||||
handler.OnTag(TAG_DATE, date);
|
||||
|
||||
/* track */
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK,
|
||||
StringFormat<16>("%u", track));
|
||||
handler.OnTag(TAG_TRACK, StringFormat<16>("%u", track));
|
||||
}
|
||||
|
||||
static bool
|
||||
sidplay_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
sidplay_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
const auto container = ParseContainerPath(path_fs);
|
||||
const unsigned song_num = container.track;
|
||||
@ -463,13 +458,12 @@ sidplay_scan_file(Path path_fs,
|
||||
const unsigned n_tracks = info.songs;
|
||||
#endif
|
||||
|
||||
ScanSidTuneInfo(info, song_num, n_tracks, handler, handler_ctx);
|
||||
ScanSidTuneInfo(info, song_num, n_tracks, handler);
|
||||
|
||||
/* time */
|
||||
const auto duration = get_song_length(tune);
|
||||
if (!duration.IsNegative())
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
SongTime(duration));
|
||||
handler.OnDuration(SongTime(duration));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -506,8 +500,8 @@ sidplay_container_scan(Path path_fs)
|
||||
for (unsigned i = 1; i <= n_tracks; ++i) {
|
||||
tune.selectSong(i);
|
||||
|
||||
ScanSidTuneInfo(info, i, n_tracks,
|
||||
add_tag_handler, &tag_builder);
|
||||
AddTagHandler h(tag_builder);
|
||||
ScanSidTuneInfo(info, i, n_tracks, h);
|
||||
|
||||
char track_name[32];
|
||||
/* Construct container/tune path names, eg.
|
||||
|
@ -240,11 +240,11 @@ sndfile_stream_decode(DecoderClient &client, InputStream &is)
|
||||
|
||||
static void
|
||||
sndfile_handle_tag(SNDFILE *sf, int str, TagType tag,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
const char *value = sf_get_string(sf, str);
|
||||
if (value != nullptr)
|
||||
tag_handler_invoke_tag(handler, handler_ctx, tag, value);
|
||||
handler.OnTag(tag, value);
|
||||
}
|
||||
|
||||
static constexpr struct {
|
||||
@ -261,8 +261,7 @@ static constexpr struct {
|
||||
};
|
||||
|
||||
static bool
|
||||
sndfile_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
sndfile_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
SF_INFO info;
|
||||
|
||||
@ -280,11 +279,10 @@ sndfile_scan_stream(InputStream &is,
|
||||
return false;
|
||||
}
|
||||
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
sndfile_duration(info));
|
||||
handler.OnDuration(sndfile_duration(info));
|
||||
|
||||
for (auto i : sndfile_tags)
|
||||
sndfile_handle_tag(sf, i.str, i.tag, handler, handler_ctx);
|
||||
sndfile_handle_tag(sf, i.str, i.tag, handler);
|
||||
|
||||
sf_close(sf);
|
||||
|
||||
|
@ -348,7 +348,7 @@ static void
|
||||
VisitVorbisDuration(InputStream &is,
|
||||
OggSyncState &sync, OggStreamState &stream,
|
||||
unsigned sample_rate,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
ogg_packet packet;
|
||||
|
||||
@ -358,12 +358,11 @@ VisitVorbisDuration(InputStream &is,
|
||||
const auto duration =
|
||||
SongTime::FromScale<uint64_t>(packet.granulepos,
|
||||
sample_rate);
|
||||
tag_handler_invoke_duration(handler, handler_ctx, duration);
|
||||
handler.OnDuration(duration);
|
||||
}
|
||||
|
||||
static bool
|
||||
vorbis_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
vorbis_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
/* initialize libogg */
|
||||
|
||||
@ -397,12 +396,11 @@ vorbis_scan_stream(InputStream &is,
|
||||
|
||||
/* visit the Vorbis comments we just read */
|
||||
|
||||
vorbis_comments_scan(vc.user_comments,
|
||||
handler, handler_ctx);
|
||||
vorbis_comments_scan(vc.user_comments, handler);
|
||||
|
||||
/* check the song duration by locating the e_o_s packet */
|
||||
|
||||
VisitVorbisDuration(is, sync, stream, vi.rate, handler, handler_ctx);
|
||||
VisitVorbisDuration(is, sync, stream, vi.rate, handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -578,8 +578,7 @@ wavpack_filedecode(DecoderClient &client, Path path_fs)
|
||||
* Reads metainfo from the specified file.
|
||||
*/
|
||||
static bool
|
||||
wavpack_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
wavpack_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
WavpackContext *wpc;
|
||||
|
||||
@ -595,14 +594,13 @@ wavpack_scan_file(Path path_fs,
|
||||
|
||||
const auto duration = GetDuration(wpc);
|
||||
if (!duration.IsNegative())
|
||||
tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
|
||||
handler.OnDuration(SongTime(duration));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
wavpack_scan_stream(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
wavpack_scan_stream(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
WavpackInput isp(nullptr, is);
|
||||
|
||||
@ -620,7 +618,7 @@ wavpack_scan_stream(InputStream &is,
|
||||
|
||||
const auto duration = GetDuration(wpc);
|
||||
if (!duration.IsNegative())
|
||||
tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration));
|
||||
handler.OnDuration(SongTime(duration));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -126,8 +126,7 @@ wildmidi_file_decode(DecoderClient &client, Path path_fs)
|
||||
}
|
||||
|
||||
static bool
|
||||
wildmidi_scan_file(Path path_fs,
|
||||
const TagHandler &handler, void *handler_ctx) noexcept
|
||||
wildmidi_scan_file(Path path_fs, TagHandler &handler) noexcept
|
||||
{
|
||||
midi *wm = WildMidi_Open(path_fs.c_str());
|
||||
if (wm == nullptr)
|
||||
@ -142,7 +141,7 @@ wildmidi_scan_file(Path path_fs,
|
||||
const auto duration =
|
||||
SongTime::FromScale<uint64_t>(info->approx_total_samples,
|
||||
WILDMIDI_SAMPLE_RATE);
|
||||
tag_handler_invoke_duration(handler, handler_ctx, duration);
|
||||
handler.OnDuration(duration);
|
||||
|
||||
WildMidi_Close(wm);
|
||||
|
||||
|
@ -53,13 +53,13 @@ vorbis_comments_to_replay_gain(ReplayGainInfo &rgi, char **comments) noexcept
|
||||
static bool
|
||||
vorbis_copy_comment(const char *comment,
|
||||
const char *name, TagType tag_type,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
const char *value;
|
||||
|
||||
value = vorbis_comment_value(comment, name);
|
||||
if (value != nullptr) {
|
||||
tag_handler_invoke_tag(handler, handler_ctx, tag_type, value);
|
||||
handler.OnTag(tag_type, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -67,36 +67,31 @@ vorbis_copy_comment(const char *comment,
|
||||
}
|
||||
|
||||
static void
|
||||
vorbis_scan_comment(const char *comment,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
vorbis_scan_comment(const char *comment, TagHandler &handler) noexcept
|
||||
{
|
||||
if (handler.pair != nullptr) {
|
||||
if (handler.WantPair()) {
|
||||
const DivideString split(comment, '=');
|
||||
if (split.IsDefined() && !split.empty())
|
||||
tag_handler_invoke_pair(handler, handler_ctx,
|
||||
split.GetFirst(),
|
||||
split.GetSecond());
|
||||
handler.OnPair(split.GetFirst(), split.GetSecond());
|
||||
}
|
||||
|
||||
for (const struct tag_table *i = xiph_tags; i->name != nullptr; ++i)
|
||||
if (vorbis_copy_comment(comment, i->name, i->type,
|
||||
handler, handler_ctx))
|
||||
handler))
|
||||
return;
|
||||
|
||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
||||
if (vorbis_copy_comment(comment,
|
||||
tag_item_names[i], TagType(i),
|
||||
handler, handler_ctx))
|
||||
handler))
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
vorbis_comments_scan(char **comments,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
vorbis_comments_scan(char **comments, TagHandler &handler) noexcept
|
||||
{
|
||||
while (*comments)
|
||||
vorbis_scan_comment(*comments++,
|
||||
handler, handler_ctx);
|
||||
vorbis_scan_comment(*comments++, handler);
|
||||
|
||||
}
|
||||
|
||||
@ -104,7 +99,8 @@ std::unique_ptr<Tag>
|
||||
vorbis_comments_to_tag(char **comments) noexcept
|
||||
{
|
||||
TagBuilder tag_builder;
|
||||
vorbis_comments_scan(comments, add_tag_handler, &tag_builder);
|
||||
AddTagHandler h(tag_builder);
|
||||
vorbis_comments_scan(comments, h);
|
||||
return tag_builder.empty()
|
||||
? nullptr
|
||||
: tag_builder.CommitNew();
|
||||
|
@ -25,15 +25,14 @@
|
||||
#include <memory>
|
||||
|
||||
struct ReplayGainInfo;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
struct Tag;
|
||||
|
||||
bool
|
||||
vorbis_comments_to_replay_gain(ReplayGainInfo &rgi, char **comments) noexcept;
|
||||
|
||||
void
|
||||
vorbis_comments_scan(char **comments,
|
||||
const TagHandler &handler, void *handler_ctx);
|
||||
vorbis_comments_scan(char **comments, TagHandler &handler) noexcept;
|
||||
|
||||
std::unique_ptr<Tag>
|
||||
vorbis_comments_to_tag(char **comments) noexcept;
|
||||
|
@ -71,22 +71,23 @@ public:
|
||||
virtual std::unique_ptr<DetachedSong> NextSong() override;
|
||||
};
|
||||
|
||||
static void
|
||||
embcue_tag_pair(const char *name, const char *value, void *ctx)
|
||||
{
|
||||
EmbeddedCuePlaylist *playlist = (EmbeddedCuePlaylist *)ctx;
|
||||
class ExtractCuesheetTagHandler final : public NullTagHandler {
|
||||
public:
|
||||
std::string cuesheet;
|
||||
|
||||
if (playlist->cuesheet.empty() &&
|
||||
StringEqualsCaseASCII(name, "cuesheet"))
|
||||
playlist->cuesheet = value;
|
||||
}
|
||||
ExtractCuesheetTagHandler() noexcept:NullTagHandler(WANT_PAIR) {}
|
||||
|
||||
static constexpr TagHandler embcue_tag_handler = {
|
||||
nullptr,
|
||||
nullptr,
|
||||
embcue_tag_pair,
|
||||
void OnPair(const char *key, const char *value) noexcept override;
|
||||
};
|
||||
|
||||
void
|
||||
ExtractCuesheetTagHandler::OnPair(const char *name, const char *value) noexcept
|
||||
{
|
||||
if (cuesheet.empty() &&
|
||||
StringEqualsCaseASCII(name, "cuesheet"))
|
||||
cuesheet = value;
|
||||
}
|
||||
|
||||
static std::unique_ptr<SongEnumerator>
|
||||
embcue_playlist_open_uri(const char *uri,
|
||||
gcc_unused Mutex &mutex)
|
||||
@ -97,18 +98,21 @@ embcue_playlist_open_uri(const char *uri,
|
||||
|
||||
const auto path_fs = AllocatedPath::FromUTF8Throw(uri);
|
||||
|
||||
auto playlist = std::make_unique<EmbeddedCuePlaylist>();
|
||||
ExtractCuesheetTagHandler extract_cuesheet;
|
||||
tag_file_scan(path_fs, extract_cuesheet);
|
||||
if (extract_cuesheet.cuesheet.empty())
|
||||
ScanGenericTags(path_fs, extract_cuesheet);
|
||||
|
||||
tag_file_scan(path_fs, embcue_tag_handler, playlist.get());
|
||||
if (playlist->cuesheet.empty())
|
||||
ScanGenericTags(path_fs, embcue_tag_handler, playlist.get());
|
||||
|
||||
if (playlist->cuesheet.empty())
|
||||
if (extract_cuesheet.cuesheet.empty())
|
||||
/* no "CUESHEET" tag found */
|
||||
return nullptr;
|
||||
|
||||
auto playlist = std::make_unique<EmbeddedCuePlaylist>();
|
||||
|
||||
playlist->filename = PathTraitsUTF8::GetBase(uri);
|
||||
|
||||
playlist->cuesheet = std::move(extract_cuesheet.cuesheet);
|
||||
|
||||
playlist->next = &playlist->cuesheet[0];
|
||||
playlist->parser = new CueParser();
|
||||
|
||||
|
@ -76,7 +76,7 @@ ForEachValue(const char *value, const char *end, C &&callback)
|
||||
static bool
|
||||
tag_ape_import_item(unsigned long flags,
|
||||
const char *key, StringView value,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
/* we only care about utf-8 text tags */
|
||||
if ((flags & (0x3 << 1)) != 0)
|
||||
@ -85,36 +85,31 @@ tag_ape_import_item(unsigned long flags,
|
||||
const auto begin = value.begin();
|
||||
const auto end = value.end();
|
||||
|
||||
if (handler.pair != nullptr)
|
||||
ForEachValue(begin, end, [handler, handler_ctx,
|
||||
key](const char *_value) {
|
||||
handler.pair(key, _value, handler_ctx);
|
||||
if (handler.WantPair())
|
||||
ForEachValue(begin, end, [&handler, key](const char *_value) {
|
||||
handler.OnPair(key, _value);
|
||||
});
|
||||
|
||||
TagType type = tag_ape_name_parse(key);
|
||||
if (type == TAG_NUM_OF_ITEM_TYPES)
|
||||
return false;
|
||||
|
||||
ForEachValue(begin, end, [handler, handler_ctx,
|
||||
type](const char *_value) {
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
type, _value);
|
||||
ForEachValue(begin, end, [&handler, type](const char *_value) {
|
||||
handler.OnTag(type, _value);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
tag_ape_scan2(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
tag_ape_scan2(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
bool recognized = false;
|
||||
|
||||
auto callback = [handler, handler_ctx, &recognized]
|
||||
auto callback = [&handler, &recognized]
|
||||
(unsigned long flags, const char *key,
|
||||
StringView value) {
|
||||
recognized |= tag_ape_import_item(flags, key, value,
|
||||
handler, handler_ctx);
|
||||
recognized |= tag_ape_import_item(flags, key, value, handler);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -20,10 +20,8 @@
|
||||
#ifndef MPD_APE_TAG_HXX
|
||||
#define MPD_APE_TAG_HXX
|
||||
|
||||
#include "Table.hxx"
|
||||
|
||||
class InputStream;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
|
||||
/**
|
||||
* Scan the APE tags of a stream.
|
||||
@ -31,7 +29,6 @@ struct TagHandler;
|
||||
* @param path_fs the path of the file in filesystem encoding
|
||||
*/
|
||||
bool
|
||||
tag_ape_scan2(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx);
|
||||
tag_ape_scan2(InputStream &is, TagHandler &handler) noexcept;
|
||||
|
||||
#endif
|
||||
|
@ -30,9 +30,9 @@
|
||||
#include <exception>
|
||||
|
||||
bool
|
||||
ScanGenericTags(InputStream &is, const TagHandler &handler, void *ctx)
|
||||
ScanGenericTags(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
if (tag_ape_scan2(is, handler, ctx))
|
||||
if (tag_ape_scan2(is, handler))
|
||||
return true;
|
||||
|
||||
#ifdef ENABLE_ID3TAG
|
||||
@ -42,19 +42,19 @@ ScanGenericTags(InputStream &is, const TagHandler &handler, void *ctx)
|
||||
return false;
|
||||
}
|
||||
|
||||
return tag_id3_scan(is, handler, ctx);
|
||||
return tag_id3_scan(is, handler);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
ScanGenericTags(Path path, const TagHandler &handler, void *ctx)
|
||||
ScanGenericTags(Path path, TagHandler &handler) noexcept
|
||||
try {
|
||||
Mutex mutex;
|
||||
|
||||
auto is = OpenLocalInputStream(path, mutex);
|
||||
return ScanGenericTags(*is, handler, ctx);
|
||||
return ScanGenericTags(*is, handler);
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
return false;
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
#include "check.h"
|
||||
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
class InputStream;
|
||||
class Path;
|
||||
|
||||
@ -31,12 +31,12 @@ class Path;
|
||||
* stream does not need to be rewound.
|
||||
*/
|
||||
bool
|
||||
ScanGenericTags(InputStream &is, const TagHandler &handler, void *ctx);
|
||||
ScanGenericTags(InputStream &is, TagHandler &handler) noexcept;
|
||||
|
||||
/**
|
||||
* Attempts to scan APE or ID3 tags from the specified file.
|
||||
*/
|
||||
bool
|
||||
ScanGenericTags(Path path, const TagHandler &handler, void *ctx);
|
||||
ScanGenericTags(Path path, TagHandler &handler) noexcept;
|
||||
|
||||
#endif
|
||||
|
@ -25,19 +25,15 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static void
|
||||
add_tag_duration(SongTime duration, void *ctx)
|
||||
void
|
||||
AddTagHandler::OnDuration(SongTime duration) noexcept
|
||||
{
|
||||
TagBuilder &tag = *(TagBuilder *)ctx;
|
||||
|
||||
tag.SetDuration(duration);
|
||||
}
|
||||
|
||||
static void
|
||||
add_tag_tag(TagType type, const char *value, void *ctx)
|
||||
void
|
||||
AddTagHandler::OnTag(TagType type, const char *value) noexcept
|
||||
{
|
||||
TagBuilder &tag = *(TagBuilder *)ctx;
|
||||
|
||||
if (type == TAG_TRACK || type == TAG_DISC) {
|
||||
/* filter out this extra data and leading zeroes */
|
||||
char *end;
|
||||
@ -48,24 +44,10 @@ add_tag_tag(TagType type, const char *value, void *ctx)
|
||||
tag.AddItem(type, value);
|
||||
}
|
||||
|
||||
const TagHandler add_tag_handler = {
|
||||
add_tag_duration,
|
||||
add_tag_tag,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
static void
|
||||
full_tag_pair(const char *name, gcc_unused const char *value, void *ctx)
|
||||
void
|
||||
FullTagHandler::OnPair(const char *name, gcc_unused const char *value) noexcept
|
||||
{
|
||||
TagBuilder &tag = *(TagBuilder *)ctx;
|
||||
|
||||
if (StringEqualsCaseASCII(name, "cuesheet"))
|
||||
tag.SetHasPlaylist(true);
|
||||
}
|
||||
|
||||
const TagHandler full_tag_handler = {
|
||||
add_tag_duration,
|
||||
add_tag_tag,
|
||||
full_tag_pair,
|
||||
};
|
||||
|
||||
|
@ -23,19 +23,45 @@
|
||||
#include "check.h"
|
||||
#include "Type.h"
|
||||
#include "Chrono.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <assert.h>
|
||||
class TagBuilder;
|
||||
|
||||
/**
|
||||
* A callback table for receiving metadata of a song.
|
||||
* An interface for receiving metadata of a song.
|
||||
*/
|
||||
struct TagHandler {
|
||||
class TagHandler {
|
||||
const unsigned want_mask;
|
||||
|
||||
public:
|
||||
static constexpr unsigned WANT_DURATION = 0x1;
|
||||
static constexpr unsigned WANT_TAG = 0x2;
|
||||
static constexpr unsigned WANT_PAIR = 0x4;
|
||||
|
||||
explicit TagHandler(unsigned _want_mask) noexcept
|
||||
:want_mask(_want_mask) {}
|
||||
|
||||
TagHandler(const TagHandler &) = delete;
|
||||
TagHandler &operator=(const TagHandler &) = delete;
|
||||
|
||||
bool WantDuration() const noexcept {
|
||||
return want_mask & WANT_DURATION;
|
||||
}
|
||||
|
||||
bool WantTag() const noexcept {
|
||||
return want_mask & WANT_TAG;
|
||||
}
|
||||
|
||||
bool WantPair() const noexcept {
|
||||
return want_mask & WANT_PAIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare the duration of a song. Do not call
|
||||
* this when the duration could not be determined, because
|
||||
* there is no magic value for "unknown duration".
|
||||
*/
|
||||
void (*duration)(SongTime duration, void *ctx);
|
||||
virtual void OnDuration(SongTime duration) noexcept = 0;
|
||||
|
||||
/**
|
||||
* A tag has been read.
|
||||
@ -43,56 +69,57 @@ struct TagHandler {
|
||||
* @param the value of the tag; the pointer will become
|
||||
* invalid after returning
|
||||
*/
|
||||
void (*tag)(TagType type, const char *value, void *ctx);
|
||||
virtual void OnTag(TagType type, const char *value) noexcept = 0;
|
||||
|
||||
/**
|
||||
* A name-value pair has been read. It is the codec specific
|
||||
* representation of tags.
|
||||
*/
|
||||
void (*pair)(const char *key, const char *value, void *ctx);
|
||||
virtual void OnPair(const char *key, const char *value) noexcept = 0;
|
||||
};
|
||||
|
||||
static inline void
|
||||
tag_handler_invoke_duration(const TagHandler &handler, void *ctx,
|
||||
SongTime duration) noexcept
|
||||
{
|
||||
if (handler.duration != nullptr)
|
||||
handler.duration(duration, ctx);
|
||||
}
|
||||
class NullTagHandler : public TagHandler {
|
||||
public:
|
||||
explicit NullTagHandler(unsigned _want_mask) noexcept
|
||||
:TagHandler(_want_mask) {}
|
||||
|
||||
static inline void
|
||||
tag_handler_invoke_tag(const TagHandler &handler, void *ctx,
|
||||
TagType type, const char *value) noexcept
|
||||
{
|
||||
assert((unsigned)type < TAG_NUM_OF_ITEM_TYPES);
|
||||
assert(value != nullptr);
|
||||
|
||||
if (handler.tag != nullptr)
|
||||
handler.tag(type, value, ctx);
|
||||
}
|
||||
|
||||
static inline void
|
||||
tag_handler_invoke_pair(const TagHandler &handler, void *ctx,
|
||||
const char *name, const char *value) noexcept
|
||||
{
|
||||
assert(name != nullptr);
|
||||
assert(value != nullptr);
|
||||
|
||||
if (handler.pair != nullptr)
|
||||
handler.pair(name, value, ctx);
|
||||
}
|
||||
void OnDuration(gcc_unused SongTime duration) noexcept override {}
|
||||
void OnTag(gcc_unused TagType type,
|
||||
gcc_unused const char *value) noexcept override {}
|
||||
void OnPair(gcc_unused const char *key,
|
||||
gcc_unused const char *value) noexcept override {}
|
||||
};
|
||||
|
||||
/**
|
||||
* This #TagHandler implementation adds tag values to a #TagBuilder object
|
||||
* (casted from the context pointer).
|
||||
* This #TagHandler implementation adds tag values to a #TagBuilder
|
||||
* object.
|
||||
*/
|
||||
extern const TagHandler add_tag_handler;
|
||||
class AddTagHandler : public NullTagHandler {
|
||||
protected:
|
||||
TagBuilder &tag;
|
||||
|
||||
AddTagHandler(unsigned _want_mask, TagBuilder &_builder) noexcept
|
||||
:NullTagHandler(_want_mask), tag(_builder) {}
|
||||
|
||||
public:
|
||||
explicit AddTagHandler(TagBuilder &_builder) noexcept
|
||||
:AddTagHandler(WANT_DURATION|WANT_TAG, _builder) {}
|
||||
|
||||
void OnDuration(SongTime duration) noexcept override;
|
||||
void OnTag(TagType type, const char *value) noexcept override;
|
||||
};
|
||||
|
||||
/**
|
||||
* This #TagHandler implementation adds tag values to a #TagBuilder object
|
||||
* (casted from the context pointer), and supports the has_playlist
|
||||
* attribute.
|
||||
*/
|
||||
extern const TagHandler full_tag_handler;
|
||||
class FullTagHandler : public AddTagHandler {
|
||||
public:
|
||||
explicit FullTagHandler(TagBuilder &_builder) noexcept
|
||||
:AddTagHandler(WANT_DURATION|WANT_TAG|WANT_PAIR, _builder) {}
|
||||
|
||||
void OnPair(const char *key, const char *value) noexcept override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -100,7 +100,7 @@ import_id3_string(const id3_ucs4_t *ucs4)
|
||||
static void
|
||||
tag_id3_import_text_frame(const struct id3_frame *frame,
|
||||
TagType type,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
if (frame->nfields != 2)
|
||||
return;
|
||||
@ -133,8 +133,7 @@ tag_id3_import_text_frame(const struct id3_frame *frame,
|
||||
|
||||
AtScopeExit(utf8) { free(utf8); };
|
||||
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
type, (const char *)utf8);
|
||||
handler.OnTag(type, (const char *)utf8);
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,13 +143,13 @@ tag_id3_import_text_frame(const struct id3_frame *frame,
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_text(struct id3_tag *tag, const char *id, TagType type,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
const struct id3_frame *frame;
|
||||
for (unsigned i = 0;
|
||||
(frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i)
|
||||
tag_id3_import_text_frame(frame, type,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,8 +163,7 @@ tag_id3_import_text(struct id3_tag *tag, const char *id, TagType type,
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type,
|
||||
const TagHandler &handler,
|
||||
void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
if (frame->nfields != 4)
|
||||
return;
|
||||
@ -185,7 +183,7 @@ tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type,
|
||||
|
||||
AtScopeExit(utf8) { free(utf8); };
|
||||
|
||||
tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8);
|
||||
handler.OnTag(type, (const char *)utf8);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,13 +192,13 @@ tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type,
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_comment(struct id3_tag *tag, const char *id, TagType type,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
const struct id3_frame *frame;
|
||||
for (unsigned i = 0;
|
||||
(frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i)
|
||||
tag_id3_import_comment_frame(frame, type,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,8 +218,7 @@ tag_id3_parse_txxx_name(const char *name) noexcept
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
|
||||
const TagHandler &handler,
|
||||
void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
for (unsigned i = 0;; ++i) {
|
||||
const id3_frame *frame = id3_tag_findframe(id3_tag, "TXXX", i);
|
||||
@ -240,15 +237,12 @@ tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
|
||||
|
||||
AtScopeExit(value) { free(value); };
|
||||
|
||||
tag_handler_invoke_pair(handler, handler_ctx,
|
||||
(const char *)name,
|
||||
(const char *)value);
|
||||
handler.OnPair((const char *)name, (const char *)value);
|
||||
|
||||
TagType type = tag_id3_parse_txxx_name((const char*)name);
|
||||
|
||||
if (type != TAG_NUM_OF_ITEM_TYPES)
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
type, (const char*)value);
|
||||
handler.OnTag(type, (const char*)value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,7 +251,7 @@ tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
|
||||
*/
|
||||
static void
|
||||
tag_id3_import_ufid(struct id3_tag *id3_tag,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
TagHandler &handler) noexcept
|
||||
{
|
||||
for (unsigned i = 0;; ++i) {
|
||||
const id3_frame *frame = id3_tag_findframe(id3_tag, "UFID", i);
|
||||
@ -284,65 +278,63 @@ tag_id3_import_ufid(struct id3_tag *id3_tag,
|
||||
continue;
|
||||
|
||||
std::string p((const char *)value, length);
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_MUSICBRAINZ_TRACKID, p.c_str());
|
||||
handler.OnTag(TAG_MUSICBRAINZ_TRACKID, p.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
scan_id3_tag(struct id3_tag *tag,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
scan_id3_tag(struct id3_tag *tag, TagHandler &handler) noexcept
|
||||
{
|
||||
tag_id3_import_text(tag, ID3_FRAME_ARTIST, TAG_ARTIST,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST,
|
||||
TAG_ALBUM_ARTIST, handler, handler_ctx);
|
||||
TAG_ALBUM_ARTIST, handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_ARTIST_SORT,
|
||||
TAG_ARTIST_SORT, handler, handler_ctx);
|
||||
TAG_ARTIST_SORT, handler);
|
||||
|
||||
tag_id3_import_text(tag, "TSOA", TAG_ALBUM_SORT, handler, handler_ctx);
|
||||
tag_id3_import_text(tag, "TSOA", TAG_ALBUM_SORT, handler);
|
||||
|
||||
tag_id3_import_text(tag, ID3_FRAME_ALBUM_ARTIST_SORT,
|
||||
TAG_ALBUM_ARTIST_SORT, handler, handler_ctx);
|
||||
TAG_ALBUM_ARTIST_SORT, handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_TITLE, TAG_TITLE,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_ALBUM, TAG_ALBUM,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_TRACK, TAG_TRACK,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_YEAR, TAG_DATE,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_ORIGINAL_RELEASE_DATE, TAG_ORIGINAL_DATE,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_GENRE, TAG_GENRE,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_COMPOSER, TAG_COMPOSER,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, "TPE3", TAG_PERFORMER,
|
||||
handler, handler_ctx);
|
||||
tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler);
|
||||
tag_id3_import_comment(tag, ID3_FRAME_COMMENT, TAG_COMMENT,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
tag_id3_import_text(tag, ID3_FRAME_DISC, TAG_DISC,
|
||||
handler, handler_ctx);
|
||||
handler);
|
||||
|
||||
tag_id3_import_musicbrainz(tag, handler, handler_ctx);
|
||||
tag_id3_import_ufid(tag, handler, handler_ctx);
|
||||
tag_id3_import_musicbrainz(tag, handler);
|
||||
tag_id3_import_ufid(tag, handler);
|
||||
}
|
||||
|
||||
std::unique_ptr<Tag>
|
||||
tag_id3_import(struct id3_tag *tag)
|
||||
{
|
||||
TagBuilder tag_builder;
|
||||
scan_id3_tag(tag, add_tag_handler, &tag_builder);
|
||||
AddTagHandler h(tag_builder);
|
||||
scan_id3_tag(tag, h);
|
||||
return tag_builder.empty()
|
||||
? nullptr
|
||||
: tag_builder.CommitNew();
|
||||
}
|
||||
|
||||
bool
|
||||
tag_id3_scan(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx)
|
||||
tag_id3_scan(InputStream &is, TagHandler &handler) noexcept
|
||||
{
|
||||
UniqueId3Tag tag;
|
||||
|
||||
@ -355,6 +347,6 @@ tag_id3_scan(InputStream &is,
|
||||
return false;
|
||||
}
|
||||
|
||||
scan_id3_tag(tag.get(), handler, handler_ctx);
|
||||
scan_id3_tag(tag.get(), handler);
|
||||
return true;
|
||||
}
|
||||
|
@ -25,13 +25,12 @@
|
||||
#include <memory>
|
||||
|
||||
class InputStream;
|
||||
struct TagHandler;
|
||||
class TagHandler;
|
||||
struct Tag;
|
||||
struct id3_tag;
|
||||
|
||||
bool
|
||||
tag_id3_scan(InputStream &is,
|
||||
const TagHandler &handler, void *handler_ctx);
|
||||
tag_id3_scan(InputStream &is, TagHandler &handler) noexcept;
|
||||
|
||||
std::unique_ptr<Tag>
|
||||
tag_id3_import(id3_tag *);
|
||||
@ -41,7 +40,6 @@ tag_id3_import(id3_tag *);
|
||||
*
|
||||
*/
|
||||
void
|
||||
scan_id3_tag(id3_tag *tag,
|
||||
const TagHandler &handler, void *handler_ctx);
|
||||
scan_id3_tag(id3_tag *tag, TagHandler &handler) noexcept;
|
||||
|
||||
#endif
|
||||
|
@ -40,31 +40,29 @@
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
static bool empty = true;
|
||||
class DumpTagHandler final : public NullTagHandler {
|
||||
bool empty = true;
|
||||
|
||||
static void
|
||||
print_duration(SongTime duration, gcc_unused void *ctx)
|
||||
{
|
||||
printf("duration=%f\n", duration.ToDoubleS());
|
||||
}
|
||||
public:
|
||||
DumpTagHandler() noexcept
|
||||
:NullTagHandler(WANT_DURATION|WANT_TAG|WANT_PAIR) {}
|
||||
|
||||
static void
|
||||
print_tag(TagType type, const char *value, gcc_unused void *ctx)
|
||||
{
|
||||
printf("[%s]=%s\n", tag_item_names[type], value);
|
||||
empty = false;
|
||||
}
|
||||
bool IsEmpty() const noexcept {
|
||||
return empty;
|
||||
}
|
||||
|
||||
static void
|
||||
print_pair(const char *name, const char *value, gcc_unused void *ctx)
|
||||
{
|
||||
printf("\"%s\"=%s\n", name, value);
|
||||
}
|
||||
void OnDuration(SongTime duration) noexcept override {
|
||||
printf("duration=%f\n", duration.ToDoubleS());
|
||||
}
|
||||
|
||||
static constexpr TagHandler print_handler = {
|
||||
print_duration,
|
||||
print_tag,
|
||||
print_pair,
|
||||
void OnTag(TagType type, const char *value) noexcept override {
|
||||
printf("[%s]=%s\n", tag_item_names[type], value);
|
||||
empty = false;
|
||||
}
|
||||
|
||||
void OnPair(const char *key, const char *value) noexcept override {
|
||||
printf("\"%s\"=%s\n", key, value);
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@ -100,9 +98,10 @@ try {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
DumpTagHandler h;
|
||||
bool success;
|
||||
try {
|
||||
success = plugin->ScanFile(path, print_handler, nullptr);
|
||||
success = plugin->ScanFile(path, h);
|
||||
} catch (const std::exception &e) {
|
||||
LogError(e);
|
||||
success = false;
|
||||
@ -113,7 +112,7 @@ try {
|
||||
|
||||
if (!success && plugin->scan_stream != NULL) {
|
||||
is = InputStream::OpenReady(path.c_str(), mutex);
|
||||
success = plugin->ScanStream(*is, print_handler, nullptr);
|
||||
success = plugin->ScanStream(*is, h);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
@ -121,11 +120,11 @@ try {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
if (h.IsEmpty()) {
|
||||
if (is)
|
||||
ScanGenericTags(*is, print_handler, nullptr);
|
||||
ScanGenericTags(*is, h);
|
||||
else
|
||||
ScanGenericTags(path, print_handler, nullptr);
|
||||
ScanGenericTags(path, h);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user