tag/Handler: convert to class with virtual methods
This commit is contained in:
+9
-14
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
+2
-5
@@ -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
|
||||
|
||||
+5
-5
@@ -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;
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+6
-24
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
+65
-38
@@ -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
|
||||
|
||||
+36
-44
@@ -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;
|
||||
}
|
||||
|
||||
+3
-5
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user