Commit c729f16d authored by Max Kellermann's avatar Max Kellermann

song/DetachedSong: copy the AudioFormat from LightSong

Enables the "Format" row in "playlistinfo" responses. https://github.com/MusicPlayerDaemon/MPD/issues/1094
parent 81d0c04e
ver 0.23 (not yet released) ver 0.23 (not yet released)
* protocol * protocol
- new command "getvol" - new command "getvol"
- show the audio format in "playlistinfo"
* output * output
- snapcast: new plugin - snapcast: new plugin
......
...@@ -111,6 +111,9 @@ song_print_info(Response &r, const DetachedSong &song, bool base) noexcept ...@@ -111,6 +111,9 @@ song_print_info(Response &r, const DetachedSong &song, bool base) noexcept
if (!IsNegative(song.GetLastModified())) if (!IsNegative(song.GetLastModified()))
time_print(r, "Last-Modified", song.GetLastModified()); time_print(r, "Last-Modified", song.GetLastModified());
if (const auto &f = song.GetAudioFormat(); f.IsDefined())
r.Format("Format: %s\n", ToString(f).c_str());
tag_print_values(r, song.GetTag()); tag_print_values(r, song.GetTag());
const auto duration = song.GetDuration(); const auto duration = song.GetDuration();
......
...@@ -86,8 +86,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song) ...@@ -86,8 +86,7 @@ song_save(BufferedOutputStream &os, const DetachedSong &song)
DetachedSong DetachedSong
song_load(TextFile &file, const char *uri, song_load(TextFile &file, const char *uri,
std::string *target_r, std::string *target_r)
AudioFormat *audio_format_r)
{ {
DetachedSong song(uri); DetachedSong song(uri);
...@@ -113,14 +112,12 @@ song_load(TextFile &file, const char *uri, ...@@ -113,14 +112,12 @@ song_load(TextFile &file, const char *uri,
if (target_r != nullptr) if (target_r != nullptr)
*target_r = value; *target_r = value;
} else if (StringIsEqual(line, "Format")) { } else if (StringIsEqual(line, "Format")) {
if (audio_format_r != nullptr) {
try { try {
*audio_format_r = song.SetAudioFormat(ParseAudioFormat(value,
ParseAudioFormat(value, false); false));
} catch (...) { } catch (...) {
/* ignore parser errors */ /* ignore parser errors */
} }
}
} else if (StringIsEqual(line, "Playlist")) { } else if (StringIsEqual(line, "Playlist")) {
tag.SetHasPlaylist(StringIsEqual(value, "yes")); tag.SetHasPlaylist(StringIsEqual(value, "yes"));
} else if (StringIsEqual(line, SONG_MTIME)) { } else if (StringIsEqual(line, SONG_MTIME)) {
......
...@@ -44,7 +44,6 @@ song_save(BufferedOutputStream &os, const DetachedSong &song); ...@@ -44,7 +44,6 @@ song_save(BufferedOutputStream &os, const DetachedSong &song);
*/ */
DetachedSong DetachedSong
song_load(TextFile &file, const char *uri, song_load(TextFile &file, const char *uri,
std::string *target_r=nullptr, std::string *target_r=nullptr);
AudioFormat *audio_format_r=nullptr);
#endif #endif
...@@ -153,9 +153,10 @@ DetachedSong::LoadFile(Path path) ...@@ -153,9 +153,10 @@ DetachedSong::LoadFile(Path path)
return false; return false;
TagBuilder tag_builder; TagBuilder tag_builder;
auto new_audio_format = AudioFormat::Undefined();
try { try {
if (!ScanFileTagsWithGeneric(path, tag_builder)) if (!ScanFileTagsWithGeneric(path, tag_builder, &new_audio_format))
return false; return false;
} catch (...) { } catch (...) {
// TODO: log or propagate I/O errors? // TODO: log or propagate I/O errors?
...@@ -163,6 +164,7 @@ DetachedSong::LoadFile(Path path) ...@@ -163,6 +164,7 @@ DetachedSong::LoadFile(Path path)
} }
mtime = fi.GetModificationTime(); mtime = fi.GetModificationTime();
audio_format = new_audio_format;
tag_builder.Commit(tag); tag_builder.Commit(tag);
return true; return true;
} }
...@@ -177,9 +179,11 @@ DetachedSong::Update() ...@@ -177,9 +179,11 @@ DetachedSong::Update()
return LoadFile(path_fs); return LoadFile(path_fs);
} else if (IsRemote()) { } else if (IsRemote()) {
TagBuilder tag_builder; TagBuilder tag_builder;
auto new_audio_format = AudioFormat::Undefined();
try { try {
if (!tag_stream_scan(uri.c_str(), tag_builder)) if (!tag_stream_scan(uri.c_str(), tag_builder,
&new_audio_format))
return false; return false;
} catch (...) { } catch (...) {
// TODO: log or propagate I/O errors? // TODO: log or propagate I/O errors?
...@@ -187,6 +191,7 @@ DetachedSong::Update() ...@@ -187,6 +191,7 @@ DetachedSong::Update()
} }
mtime = std::chrono::system_clock::time_point::min(); mtime = std::chrono::system_clock::time_point::min();
audio_format = new_audio_format;
tag_builder.Commit(tag); tag_builder.Commit(tag);
return true; return true;
} else } else
......
...@@ -168,15 +168,12 @@ directory_load(TextFile &file, Directory &directory) ...@@ -168,15 +168,12 @@ directory_load(TextFile &file, Directory &directory)
throw FormatRuntimeError("Duplicate song '%s'", name); throw FormatRuntimeError("Duplicate song '%s'", name);
std::string target; std::string target;
auto audio_format = AudioFormat::Undefined();
auto detached_song = song_load(file, name, auto detached_song = song_load(file, name,
&target, &target);
&audio_format);
auto song = std::make_unique<Song>(std::move(detached_song), auto song = std::make_unique<Song>(std::move(detached_song),
directory); directory);
song->target = std::move(target); song->target = std::move(target);
song->audio_format = audio_format;
directory.AddSong(std::move(song)); directory.AddSong(std::move(song));
} else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) { } else if ((p = StringAfterPrefix(line, PLAYLIST_META_BEGIN))) {
......
...@@ -34,6 +34,7 @@ Song::Song(DetachedSong &&other, Directory &_parent) noexcept ...@@ -34,6 +34,7 @@ Song::Song(DetachedSong &&other, Directory &_parent) noexcept
mtime(other.GetLastModified()), mtime(other.GetLastModified()),
start_time(other.GetStartTime()), start_time(other.GetStartTime()),
end_time(other.GetEndTime()), end_time(other.GetEndTime()),
audio_format(other.GetAudioFormat()),
filename(other.GetURI()) filename(other.GetURI())
{ {
} }
......
...@@ -47,6 +47,9 @@ merge_song_metadata(DetachedSong &add, const DetachedSong &base) noexcept ...@@ -47,6 +47,9 @@ merge_song_metadata(DetachedSong &add, const DetachedSong &base) noexcept
if (add.GetEndTime().IsZero()) { if (add.GetEndTime().IsZero()) {
add.SetEndTime(base.GetEndTime()); add.SetEndTime(base.GetEndTime());
} }
if (!add.GetAudioFormat().IsDefined())
add.SetAudioFormat(base.GetAudioFormat());
} }
static bool static bool
......
...@@ -28,7 +28,8 @@ DetachedSong::DetachedSong(const LightSong &other) noexcept ...@@ -28,7 +28,8 @@ DetachedSong::DetachedSong(const LightSong &other) noexcept
tag(other.tag), tag(other.tag),
mtime(other.mtime), mtime(other.mtime),
start_time(other.start_time), start_time(other.start_time),
end_time(other.end_time) {} end_time(other.end_time),
audio_format(other.audio_format) {}
DetachedSong::operator LightSong() const noexcept DetachedSong::operator LightSong() const noexcept
{ {
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define MPD_DETACHED_SONG_HXX #define MPD_DETACHED_SONG_HXX
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "pcm/AudioFormat.hxx"
#include "Chrono.hxx" #include "Chrono.hxx"
#include "util/Compiler.h" #include "util/Compiler.h"
...@@ -79,6 +80,12 @@ class DetachedSong { ...@@ -79,6 +80,12 @@ class DetachedSong {
*/ */
SongTime end_time = SongTime::zero(); SongTime end_time = SongTime::zero();
/**
* The audio format of the song, if given by the decoder
* plugin. May be undefined if unknown.
*/
AudioFormat audio_format = AudioFormat::Undefined();
public: public:
explicit DetachedSong(const char *_uri) noexcept explicit DetachedSong(const char *_uri) noexcept
:uri(_uri) {} :uri(_uri) {}
...@@ -231,6 +238,14 @@ public: ...@@ -231,6 +238,14 @@ public:
gcc_pure gcc_pure
SignedSongTime GetDuration() const noexcept; SignedSongTime GetDuration() const noexcept;
const AudioFormat &GetAudioFormat() const noexcept {
return audio_format;
}
void SetAudioFormat(const AudioFormat &src) noexcept {
audio_format = src;
}
/** /**
* Update the #tag and #mtime. * Update the #tag and #mtime.
* *
......
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