Commit 7c25d83f authored by Max Kellermann's avatar Max Kellermann

Tag: use SignedSongTime for the song duration

parent 8ce30c6a
...@@ -58,11 +58,16 @@ DetachedSong::IsInDatabase() const ...@@ -58,11 +58,16 @@ DetachedSong::IsInDatabase() const
return !uri_has_scheme(_uri) && !PathTraitsUTF8::IsAbsolute(_uri); return !uri_has_scheme(_uri) && !PathTraitsUTF8::IsAbsolute(_uri);
} }
double SignedSongTime
DetachedSong::GetDuration() const DetachedSong::GetDuration() const
{ {
if (end_time.IsPositive()) SongTime a = start_time, b = end_time;
return (end_time - start_time).ToDoubleS(); if (!b.IsPositive()) {
if (tag.duration.IsNegative())
return tag.duration;
return tag.time - start_time.ToDoubleS(); b = SongTime(tag.duration);
}
return SignedSongTime(b - a);
} }
...@@ -213,7 +213,7 @@ public: ...@@ -213,7 +213,7 @@ public:
} }
gcc_pure gcc_pure
double GetDuration() const; SignedSongTime GetDuration() const;
/** /**
* Update the #tag and #mtime. * Update the #tag and #mtime.
......
...@@ -349,7 +349,7 @@ Player::WaitForDecoder() ...@@ -349,7 +349,7 @@ Player::WaitForDecoder()
decoder_starting = true; decoder_starting = true;
/* update PlayerControl's song information */ /* update PlayerControl's song information */
pc.total_time = pc.next_song->GetDuration(); pc.total_time = pc.next_song->GetDuration().ToDoubleS();
pc.bit_rate = 0; pc.bit_rate = 0;
pc.audio_format.Clear(); pc.audio_format.Clear();
...@@ -374,7 +374,7 @@ real_song_duration(const DetachedSong &song, double decoder_duration) ...@@ -374,7 +374,7 @@ real_song_duration(const DetachedSong &song, double decoder_duration)
if (decoder_duration <= 0.0) if (decoder_duration <= 0.0)
/* the decoder plugin didn't provide information; fall /* the decoder plugin didn't provide information; fall
back to Song::GetDuration() */ back to Song::GetDuration() */
return song.GetDuration(); return song.GetDuration().ToDoubleS();
const SongTime start_time = song.GetStartTime(); const SongTime start_time = song.GetStartTime();
const SongTime end_time = song.GetEndTime(); const SongTime end_time = song.GetEndTime();
......
...@@ -119,7 +119,7 @@ song_print_info(Client &client, const DetachedSong &song, bool base) ...@@ -119,7 +119,7 @@ song_print_info(Client &client, const DetachedSong &song, bool base)
tag_print_values(client, song.GetTag()); tag_print_values(client, song.GetTag());
double duration = song.GetDuration(); const auto duration = song.GetDuration();
if (duration >= 0) if (!duration.IsNegative())
client_printf(client, "Time: %u\n", unsigned(duration + 0.5)); client_printf(client, "Time: %u\n", duration.RoundS());
} }
...@@ -100,7 +100,7 @@ song_load(TextFile &file, const char *uri, ...@@ -100,7 +100,7 @@ song_load(TextFile &file, const char *uri,
if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) { if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) {
tag.AddItem(type, value); tag.AddItem(type, value);
} else if (strcmp(line, "Time") == 0) { } else if (strcmp(line, "Time") == 0) {
tag.SetTime(atoi(value)); tag.SetDuration(SignedSongTime::FromS(atof(value)));
} else if (strcmp(line, "Playlist") == 0) { } else if (strcmp(line, "Playlist") == 0) {
tag.SetHasPlaylist(strcmp(value, "yes") == 0); tag.SetHasPlaylist(strcmp(value, "yes") == 0);
} else if (strcmp(line, SONG_MTIME) == 0) { } else if (strcmp(line, SONG_MTIME) == 0) {
......
...@@ -52,8 +52,8 @@ tag_print_values(Client &client, const Tag &tag) ...@@ -52,8 +52,8 @@ tag_print_values(Client &client, const Tag &tag)
void tag_print(Client &client, const Tag &tag) void tag_print(Client &client, const Tag &tag)
{ {
if (tag.time >= 0) if (!tag.duration.IsNegative())
client_printf(client, SONG_TIME "%i\n", tag.time); client_printf(client, SONG_TIME "%i\n", tag.duration.RoundS());
tag_print_values(client, tag); tag_print_values(client, tag);
} }
...@@ -27,8 +27,8 @@ ...@@ -27,8 +27,8 @@
void void
tag_save(BufferedOutputStream &os, const Tag &tag) tag_save(BufferedOutputStream &os, const Tag &tag)
{ {
if (tag.time >= 0) if (!tag.duration.IsNegative())
os.Format(SONG_TIME "%i\n", tag.time); os.Format(SONG_TIME "%f\n", tag.duration.ToDoubleS());
if (tag.has_playlist) if (tag.has_playlist)
os.Format("Playlist: yes\n"); os.Format("Playlist: yes\n");
......
...@@ -64,7 +64,10 @@ static bool ...@@ -64,7 +64,10 @@ static bool
stats_visitor_song(SearchStats &stats, const LightSong &song) stats_visitor_song(SearchStats &stats, const LightSong &song)
{ {
stats.n_songs++; stats.n_songs++;
stats.total_time_s += song.GetDuration();
const auto duration = song.GetDuration();
if (!duration.IsNegative())
stats.total_time_s += duration.ToS();
return true; return true;
} }
...@@ -79,8 +82,8 @@ CollectGroupCounts(TagCountMap &map, TagType group, const Tag &tag) ...@@ -79,8 +82,8 @@ CollectGroupCounts(TagCountMap &map, TagType group, const Tag &tag)
SearchStats())); SearchStats()));
SearchStats &s = r.first->second; SearchStats &s = r.first->second;
++s.n_songs; ++s.n_songs;
if (tag.time > 0) if (!tag.duration.IsNegative())
s.total_time_s += tag.time; s.total_time_s += tag.duration.ToS();
found = true; found = true;
} }
......
...@@ -40,8 +40,8 @@ static void ...@@ -40,8 +40,8 @@ static void
StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums, StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
const Tag &tag) const Tag &tag)
{ {
if (tag.time > 0) if (!tag.duration.IsNegative())
stats.total_duration += tag.time; stats.total_duration += tag.duration.ToS();
for (const auto &item : tag) { for (const auto &item : tag) {
switch (item.type) { switch (item.type) {
......
...@@ -20,14 +20,16 @@ ...@@ -20,14 +20,16 @@
#include "LightSong.hxx" #include "LightSong.hxx"
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
double SignedSongTime
LightSong::GetDuration() const LightSong::GetDuration() const
{ {
if (end_time.IsPositive()) SongTime a = start_time, b = end_time;
return (end_time - start_time).ToDoubleS(); if (!b.IsPositive()) {
if (tag->duration.IsNegative())
return tag->duration;
if (tag->time <= 0) b = SongTime(tag->duration);
return 0; }
return tag->time - start_time.ToDoubleS(); return SignedSongTime(b - a);
} }
...@@ -87,7 +87,7 @@ struct LightSong { ...@@ -87,7 +87,7 @@ struct LightSong {
} }
gcc_pure gcc_pure
double GetDuration() const; SignedSongTime GetDuration() const;
}; };
#endif #endif
...@@ -199,7 +199,10 @@ ProxySong::ProxySong(const mpd_song *song) ...@@ -199,7 +199,10 @@ ProxySong::ProxySong(const mpd_song *song)
#endif #endif
TagBuilder tag_builder; TagBuilder tag_builder;
tag_builder.SetTime(mpd_song_get_duration(song));
const unsigned duration = mpd_song_get_duration(song);
if (duration > 0)
tag_builder.SetDuration(SignedSongTime::FromS(duration));
for (const auto *i = &tag_table[0]; i->d != TAG_NUM_OF_ITEM_TYPES; ++i) for (const auto *i = &tag_table[0]; i->d != TAG_NUM_OF_ITEM_TYPES; ++i)
Copy(tag_builder, i->d, song, i->s); Copy(tag_builder, i->d, song, i->s);
......
...@@ -59,28 +59,28 @@ ParseItemClass(const char *name, size_t length) ...@@ -59,28 +59,28 @@ ParseItemClass(const char *name, size_t length)
} }
gcc_pure gcc_pure
static int static SignedSongTime
ParseDuration(const char *duration) ParseDuration(const char *duration)
{ {
char *endptr; char *endptr;
unsigned result = ParseUnsigned(duration, &endptr); unsigned result = ParseUnsigned(duration, &endptr);
if (endptr == duration || *endptr != ':') if (endptr == duration || *endptr != ':')
return 0; return SignedSongTime::Negative();
result *= 60; result *= 60;
duration = endptr + 1; duration = endptr + 1;
result += ParseUnsigned(duration, &endptr); result += ParseUnsigned(duration, &endptr);
if (endptr == duration || *endptr != ':') if (endptr == duration || *endptr != ':')
return 0; return SignedSongTime::Negative();
result *= 60; result *= 60;
duration = endptr + 1; duration = endptr + 1;
result += ParseUnsigned(duration, &endptr); result += ParseUnsigned(duration, &endptr);
if (endptr == duration || *endptr != 0) if (endptr == duration || *endptr != 0)
return 0; return SignedSongTime::Negative();
return result; return SignedSongTime::FromS(result);
} }
/** /**
...@@ -183,7 +183,7 @@ protected: ...@@ -183,7 +183,7 @@ protected:
const char *duration = const char *duration =
GetAttribute(attrs, "duration"); GetAttribute(attrs, "duration");
if (duration != nullptr) if (duration != nullptr)
tag.SetTime(ParseDuration(duration)); tag.SetDuration(ParseDuration(duration));
state = RES; state = RES;
} }
......
...@@ -105,7 +105,7 @@ mpd_despotify_tag_from_track(const ds_track &track) ...@@ -105,7 +105,7 @@ mpd_despotify_tag_from_track(const ds_track &track)
tag.AddItem(TAG_ALBUM, track.album); tag.AddItem(TAG_ALBUM, track.album);
tag.AddItem(TAG_DATE, date); tag.AddItem(TAG_DATE, date);
tag.AddItem(TAG_COMMENT, comment); tag.AddItem(TAG_COMMENT, comment);
tag.SetTime(track.length / 1000); tag.SetDuration(SignedSongTime::FromMS(track.length));
return tag.Commit(); return tag.Commit();
} }
......
...@@ -363,16 +363,20 @@ RoarOutput::SendTag(const Tag &tag) ...@@ -363,16 +363,20 @@ RoarOutput::SendTag(const Tag &tag)
const ScopeLock protect(mutex); const ScopeLock protect(mutex);
size_t cnt = 1; size_t cnt = 0;
struct roar_keyval vals[32]; struct roar_keyval vals[32];
char uuid_buf[32][64]; char uuid_buf[32][64];
char timebuf[16]; char timebuf[16];
snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", if (!tag.duration.IsNegative()) {
tag.time / 3600, (tag.time % 3600) / 60, tag.time % 60); const unsigned seconds = tag.duration.ToS();
snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d",
vals[0].key = const_cast<char *>("LENGTH"); seconds / 3600, (seconds % 3600) / 60, seconds % 60);
vals[0].value = timebuf;
vals[cnt].key = const_cast<char *>("LENGTH");
vals[cnt].value = timebuf;
++cnt;
}
for (const auto &item : tag) { for (const auto &item : tag) {
if (cnt >= 32) if (cnt >= 32)
......
...@@ -89,7 +89,7 @@ extm3u_parse_tag(const char *line) ...@@ -89,7 +89,7 @@ extm3u_parse_tag(const char *line)
return Tag(); return Tag();
TagBuilder tag; TagBuilder tag;
tag.SetTime(duration); tag.SetDuration(SignedSongTime::FromS(unsigned(duration)));
/* unfortunately, there is no real specification for the /* unfortunately, there is no real specification for the
EXTM3U format, so we must assume that the string after the EXTM3U format, so we must assume that the string after the
......
...@@ -85,7 +85,7 @@ pls_parser(GKeyFile *keyfile, std::forward_list<DetachedSong> &songs) ...@@ -85,7 +85,7 @@ pls_parser(GKeyFile *keyfile, std::forward_list<DetachedSong> &songs)
int length = g_key_file_get_integer(keyfile, "playlist", key, int length = g_key_file_get_integer(keyfile, "playlist", key,
nullptr); nullptr);
if (length > 0) if (length > 0)
tag.SetTime(length); tag.SetDuration(SignedSongTime::FromS(length));
songs.emplace_front(uri, tag.Commit()); songs.emplace_front(uri, tag.Commit());
g_free(uri); g_free(uri);
......
...@@ -215,7 +215,7 @@ handle_end_map(void *ctx) ...@@ -215,7 +215,7 @@ handle_end_map(void *ctx)
soundcloud_config.apikey.c_str(), nullptr); soundcloud_config.apikey.c_str(), nullptr);
TagBuilder tag; TagBuilder tag;
tag.SetTime(data->duration / 1000); tag.SetDuration(SignedSongTime::FromMS(data->duration));
if (data->title != nullptr) if (data->title != nullptr)
tag.AddItem(TAG_NAME, data->title); tag.AddItem(TAG_NAME, data->title);
......
...@@ -463,18 +463,19 @@ playlist::SetSongIdRange(PlayerControl &pc, unsigned id, ...@@ -463,18 +463,19 @@ playlist::SetSongIdRange(PlayerControl &pc, unsigned id,
} }
DetachedSong &song = queue.Get(position); DetachedSong &song = queue.Get(position);
if (song.GetTag().time > 0) {
const auto duration = song.GetTag().duration;
if (!duration.IsNegative()) {
/* validate the offsets */ /* validate the offsets */
const unsigned duration = song.GetTag().time; if (start > duration) {
if (start.ToMS() / 1000u > duration) {
error.Set(playlist_domain, error.Set(playlist_domain,
int(PlaylistResult::BAD_RANGE), int(PlaylistResult::BAD_RANGE),
"Invalid start offset"); "Invalid start offset");
return false; return false;
} }
if (end.ToMS() / 1000u > duration) if (end >= duration)
end = SongTime::zero(); end = SongTime::zero();
} }
......
...@@ -61,7 +61,7 @@ tag_name_parse_i(const char *name) ...@@ -61,7 +61,7 @@ tag_name_parse_i(const char *name)
void void
Tag::Clear() Tag::Clear()
{ {
time = -1; duration = SignedSongTime::Negative();
has_playlist = false; has_playlist = false;
tag_pool_lock.lock(); tag_pool_lock.lock();
...@@ -75,7 +75,7 @@ Tag::Clear() ...@@ -75,7 +75,7 @@ Tag::Clear()
} }
Tag::Tag(const Tag &other) Tag::Tag(const Tag &other)
:time(other.time), has_playlist(other.has_playlist), :duration(other.duration), has_playlist(other.has_playlist),
num_items(other.num_items), num_items(other.num_items),
items(nullptr) items(nullptr)
{ {
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "TagType.h" // IWYU pragma: export #include "TagType.h" // IWYU pragma: export
#include "TagItem.hxx" // IWYU pragma: export #include "TagItem.hxx" // IWYU pragma: export
#include "Chrono.hxx"
#include "Compiler.h" #include "Compiler.h"
#include <algorithm> #include <algorithm>
...@@ -35,12 +36,10 @@ ...@@ -35,12 +36,10 @@
*/ */
struct Tag { struct Tag {
/** /**
* The duration of the song (in seconds). A value of zero * The duration of the song. A negative value means that the
* means that the length is unknown. If the duration is * length is unknown.
* really between zero and one second, you should round up to
* 1.
*/ */
int time; SignedSongTime duration;
/** /**
* Does this file have an embedded playlist (e.g. embedded CUE * Does this file have an embedded playlist (e.g. embedded CUE
...@@ -57,13 +56,13 @@ struct Tag { ...@@ -57,13 +56,13 @@ struct Tag {
/** /**
* Create an empty tag. * Create an empty tag.
*/ */
Tag():time(-1), has_playlist(false), Tag():duration(SignedSongTime::Negative()), has_playlist(false),
num_items(0), items(nullptr) {} num_items(0), items(nullptr) {}
Tag(const Tag &other); Tag(const Tag &other);
Tag(Tag &&other) Tag(Tag &&other)
:time(other.time), has_playlist(other.has_playlist), :duration(other.duration), has_playlist(other.has_playlist),
num_items(other.num_items), items(other.items) { num_items(other.num_items), items(other.items) {
other.items = nullptr; other.items = nullptr;
other.num_items = 0; other.num_items = 0;
...@@ -79,7 +78,7 @@ struct Tag { ...@@ -79,7 +78,7 @@ struct Tag {
Tag &operator=(const Tag &other) = delete; Tag &operator=(const Tag &other) = delete;
Tag &operator=(Tag &&other) { Tag &operator=(Tag &&other) {
time = other.time; duration = other.duration;
has_playlist = other.has_playlist; has_playlist = other.has_playlist;
std::swap(items, other.items); std::swap(items, other.items);
std::swap(num_items, other.num_items); std::swap(num_items, other.num_items);
...@@ -87,8 +86,8 @@ struct Tag { ...@@ -87,8 +86,8 @@ struct Tag {
} }
/** /**
* Returns true if the tag contains no items. This ignores the "time" * Returns true if the tag contains no items. This ignores
* attribute. * the "duration" attribute.
*/ */
bool IsEmpty() const { bool IsEmpty() const {
return num_items == 0; return num_items == 0;
...@@ -98,7 +97,7 @@ struct Tag { ...@@ -98,7 +97,7 @@ struct Tag {
* Returns true if the tag contains any information. * Returns true if the tag contains any information.
*/ */
bool IsDefined() const { bool IsDefined() const {
return !IsEmpty() || time >= 0; return !IsEmpty() || !duration.IsNegative();
} }
/** /**
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#include <stdlib.h> #include <stdlib.h>
TagBuilder::TagBuilder(const Tag &other) TagBuilder::TagBuilder(const Tag &other)
:time(other.time), has_playlist(other.has_playlist) :duration(other.duration), has_playlist(other.has_playlist)
{ {
items.reserve(other.num_items); items.reserve(other.num_items);
...@@ -40,7 +40,7 @@ TagBuilder::TagBuilder(const Tag &other) ...@@ -40,7 +40,7 @@ TagBuilder::TagBuilder(const Tag &other)
} }
TagBuilder::TagBuilder(Tag &&other) TagBuilder::TagBuilder(Tag &&other)
:time(other.time), has_playlist(other.has_playlist) :duration(other.duration), has_playlist(other.has_playlist)
{ {
/* move all TagItem pointers from the Tag object; we don't /* move all TagItem pointers from the Tag object; we don't
need to contact the tag pool, because all we do is move need to contact the tag pool, because all we do is move
...@@ -58,7 +58,7 @@ TagBuilder & ...@@ -58,7 +58,7 @@ TagBuilder &
TagBuilder::operator=(const TagBuilder &other) TagBuilder::operator=(const TagBuilder &other)
{ {
/* copy all attributes */ /* copy all attributes */
time = other.time; duration = other.duration;
has_playlist = other.has_playlist; has_playlist = other.has_playlist;
items = other.items; items = other.items;
...@@ -74,7 +74,7 @@ TagBuilder::operator=(const TagBuilder &other) ...@@ -74,7 +74,7 @@ TagBuilder::operator=(const TagBuilder &other)
TagBuilder & TagBuilder &
TagBuilder::operator=(TagBuilder &&other) TagBuilder::operator=(TagBuilder &&other)
{ {
time = other.time; duration = other.duration;
has_playlist = other.has_playlist; has_playlist = other.has_playlist;
items = std::move(other.items); items = std::move(other.items);
...@@ -84,7 +84,7 @@ TagBuilder::operator=(TagBuilder &&other) ...@@ -84,7 +84,7 @@ TagBuilder::operator=(TagBuilder &&other)
TagBuilder & TagBuilder &
TagBuilder::operator=(Tag &&other) TagBuilder::operator=(Tag &&other)
{ {
time = other.time; duration = other.duration;
has_playlist = other.has_playlist; has_playlist = other.has_playlist;
/* move all TagItem pointers from the Tag object; we don't /* move all TagItem pointers from the Tag object; we don't
...@@ -105,7 +105,7 @@ TagBuilder::operator=(Tag &&other) ...@@ -105,7 +105,7 @@ TagBuilder::operator=(Tag &&other)
void void
TagBuilder::Clear() TagBuilder::Clear()
{ {
time = -1; duration = SignedSongTime::Negative();
has_playlist = false; has_playlist = false;
RemoveAll(); RemoveAll();
} }
...@@ -115,7 +115,7 @@ TagBuilder::Commit(Tag &tag) ...@@ -115,7 +115,7 @@ TagBuilder::Commit(Tag &tag)
{ {
tag.Clear(); tag.Clear();
tag.time = time; tag.duration = duration;
tag.has_playlist = has_playlist; tag.has_playlist = has_playlist;
/* move all TagItem pointers to the new Tag object without /* move all TagItem pointers to the new Tag object without
...@@ -162,8 +162,8 @@ TagBuilder::HasType(TagType type) const ...@@ -162,8 +162,8 @@ TagBuilder::HasType(TagType type) const
void void
TagBuilder::Complement(const Tag &other) TagBuilder::Complement(const Tag &other)
{ {
if (time <= 0) if (duration.IsNegative())
time = other.time; duration = other.duration;
has_playlist |= other.has_playlist; has_playlist |= other.has_playlist;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define MPD_TAG_BUILDER_HXX #define MPD_TAG_BUILDER_HXX
#include "TagType.h" #include "TagType.h"
#include "Chrono.hxx"
#include "Compiler.h" #include "Compiler.h"
#include <vector> #include <vector>
...@@ -35,12 +36,10 @@ struct Tag; ...@@ -35,12 +36,10 @@ struct Tag;
*/ */
class TagBuilder { class TagBuilder {
/** /**
* The duration of the song (in seconds). A value of zero * The duration of the song. A negative value means that the
* means that the length is unknown. If the duration is * length is unknown.
* really between zero and one second, you should round up to
* 1.
*/ */
int time; SignedSongTime duration;
/** /**
* Does this file have an embedded playlist (e.g. embedded CUE * Does this file have an embedded playlist (e.g. embedded CUE
...@@ -56,7 +55,7 @@ public: ...@@ -56,7 +55,7 @@ public:
* Create an empty tag. * Create an empty tag.
*/ */
TagBuilder() TagBuilder()
:time(-1), has_playlist(false) {} :duration(SignedSongTime::Negative()), has_playlist(false) {}
~TagBuilder() { ~TagBuilder() {
Clear(); Clear();
...@@ -73,8 +72,8 @@ public: ...@@ -73,8 +72,8 @@ public:
TagBuilder &operator=(Tag &&other); TagBuilder &operator=(Tag &&other);
/** /**
* Returns true if the tag contains no items. This ignores the "time" * Returns true if the tag contains no items. This ignores
* attribute. * the "duration" attribute.
*/ */
bool IsEmpty() const { bool IsEmpty() const {
return items.empty(); return items.empty();
...@@ -85,7 +84,7 @@ public: ...@@ -85,7 +84,7 @@ public:
*/ */
gcc_pure gcc_pure
bool IsDefined() const { bool IsDefined() const {
return time >= 0 || has_playlist || !IsEmpty(); return !duration.IsNegative() || has_playlist || !IsEmpty();
} }
void Clear(); void Clear();
...@@ -109,8 +108,8 @@ public: ...@@ -109,8 +108,8 @@ public:
*/ */
Tag *CommitNew(); Tag *CommitNew();
void SetTime(int _time) { void SetDuration(SignedSongTime _duration) {
time = _time; duration = _duration;
} }
void SetHasPlaylist(bool _has_playlist) { void SetHasPlaylist(bool _has_playlist) {
......
...@@ -27,7 +27,7 @@ add_tag_duration(unsigned seconds, void *ctx) ...@@ -27,7 +27,7 @@ add_tag_duration(unsigned seconds, void *ctx)
{ {
TagBuilder &tag = *(TagBuilder *)ctx; TagBuilder &tag = *(TagBuilder *)ctx;
tag.SetTime(seconds); tag.SetDuration(SignedSongTime::FromS(seconds));
} }
static void static void
......
...@@ -155,10 +155,13 @@ Client::AllowFile(gcc_unused Path path_fs, gcc_unused Error &error) const ...@@ -155,10 +155,13 @@ Client::AllowFile(gcc_unused Path path_fs, gcc_unused Error &error) const
static std::string static std::string
ToString(const Tag &tag) ToString(const Tag &tag)
{ {
char buffer[64]; std::string result;
sprintf(buffer, "%d", tag.time);
std::string result = buffer; if (!tag.duration.IsNegative()) {
char buffer[64];
sprintf(buffer, "%d", tag.duration.ToMS());
result.append(buffer);
}
for (const auto &item : tag) { for (const auto &item : tag) {
result.push_back('|'); result.push_back('|');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment