diff --git a/NEWS b/NEWS index d1cbabb8a..512f59e6b 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,12 @@ ver 0.22 (not yet released) - ffmpeg: new plugin based on FFmpeg's libavfilter library - hdcd: new plugin based on FFmpeg's "af_hdcd" for HDCD playback +ver 0.21.12 (not yet released) +* decoder + - opus: ignore case in replay gain tag names +* Windows + - support backslash in relative URIs loaded from playlists + ver 0.21.11 (2019/07/03) * input - tidal: deprecated because Tidal has changed the protocol diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 9384fd838..84455850c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="35" + android:versionName="0.21.12"> diff --git a/src/decoder/plugins/OpusTags.cxx b/src/decoder/plugins/OpusTags.cxx index d0daf4c70..ce4e8f6ef 100644 --- a/src/decoder/plugins/OpusTags.cxx +++ b/src/decoder/plugins/OpusTags.cxx @@ -22,6 +22,7 @@ #include "lib/xiph/XiphTags.hxx" #include "tag/Handler.hxx" #include "tag/ParseName.hxx" +#include "util/ASCII.hxx" #include "ReplayGainInfo.hxx" #include "util/NumberParser.hxx" #include "util/StringView.hxx" @@ -44,7 +45,7 @@ ScanOneOpusTag(StringView name, StringView value, ReplayGainInfo *rgi, TagHandler &handler) noexcept { - if (rgi != nullptr && name.Equals("R128_TRACK_GAIN")) { + if (rgi != nullptr && name.EqualsIgnoreCase("R128_TRACK_GAIN")) { /* R128_TRACK_GAIN is a Q7.8 fixed point number in dB */ @@ -52,7 +53,8 @@ ScanOneOpusTag(StringView name, StringView value, const auto l = ParseInt64(value, &endptr, 10); if (endptr > value.begin() && endptr == value.end()) rgi->track.gain = double(l) / 256.; - } else if (rgi != nullptr && name.Equals("R128_ALBUM_GAIN")) { + } else if (rgi != nullptr && + name.EqualsIgnoreCase("R128_ALBUM_GAIN")) { /* R128_ALBUM_GAIN is a Q7.8 fixed point number in dB */ diff --git a/src/playlist/PlaylistSong.cxx b/src/playlist/PlaylistSong.cxx index 48153f22c..f23cfee2b 100644 --- a/src/playlist/PlaylistSong.cxx +++ b/src/playlist/PlaylistSong.cxx @@ -25,6 +25,9 @@ #include "util/UriUtil.hxx" #include "song/DetachedSong.hxx" +#include +#include + #include static void @@ -66,6 +69,22 @@ playlist_check_translate_song(DetachedSong &song, const char *base_uri, base_uri = nullptr; const char *uri = song.GetURI(); + +#ifdef _WIN32 + if (!PathTraitsUTF8::IsAbsolute(uri) && strchr(uri, '\\') != nullptr) { + /* Windows uses the backslash as path separator, but + the MPD protocol uses the (forward) slash by + definition; to allow backslashes in relative URIs + loaded from playlist files, this step converts all + backslashes to (forward) slashes */ + + std::string new_uri(uri); + std::replace(new_uri.begin(), new_uri.end(), '\\', '/'); + song.SetURI(std::move(new_uri)); + uri = song.GetURI(); + } +#endif + if (base_uri != nullptr && !uri_has_scheme(uri) && !PathTraitsUTF8::IsAbsolute(uri)) song.SetURI(PathTraitsUTF8::Build(base_uri, uri)); diff --git a/test/RunChromaprint.cxx b/test/RunChromaprint.cxx index 4e8b03148..abcce7696 100644 --- a/test/RunChromaprint.cxx +++ b/test/RunChromaprint.cxx @@ -23,6 +23,7 @@ #include "event/Thread.hxx" #include "decoder/DecoderList.hxx" #include "decoder/DecoderPlugin.hxx" +#include "decoder/DecoderAPI.hxx" /* for class StopDecoder */ #include "input/Init.hxx" #include "input/InputStream.hxx" #include "fs/Path.hxx" @@ -126,10 +127,16 @@ try { MyChromaprintDecoderClient client; if (plugin->file_decode != nullptr) { - plugin->FileDecode(client, Path::FromFS(c.uri)); + try { + plugin->FileDecode(client, Path::FromFS(c.uri)); + } catch (StopDecoder) { + } } else if (plugin->stream_decode != nullptr) { auto is = InputStream::OpenReady(c.uri, client.mutex); - plugin->StreamDecode(client, *is); + try { + plugin->StreamDecode(client, *is); + } catch (StopDecoder) { + } } else { fprintf(stderr, "Decoder plugin is not usable\n"); return EXIT_FAILURE; diff --git a/test/run_decoder.cxx b/test/run_decoder.cxx index c94b5ee07..f7e3d6739 100644 --- a/test/run_decoder.cxx +++ b/test/run_decoder.cxx @@ -21,6 +21,7 @@ #include "event/Thread.hxx" #include "decoder/DecoderList.hxx" #include "decoder/DecoderPlugin.hxx" +#include "decoder/DecoderAPI.hxx" /* for class StopDecoder */ #include "DumpDecoderClient.hxx" #include "input/Init.hxx" #include "input/InputStream.hxx" @@ -116,10 +117,16 @@ try { DumpDecoderClient client; if (plugin->file_decode != nullptr) { - plugin->FileDecode(client, Path::FromFS(c.uri)); + try { + plugin->FileDecode(client, Path::FromFS(c.uri)); + } catch (StopDecoder) { + } } else if (plugin->stream_decode != nullptr) { auto is = InputStream::OpenReady(c.uri, client.mutex); - plugin->StreamDecode(client, *is); + try { + plugin->StreamDecode(client, *is); + } catch (StopDecoder) { + } } else { fprintf(stderr, "Decoder plugin is not usable\n"); return EXIT_FAILURE; diff --git a/test/test_translate_song.cxx b/test/test_translate_song.cxx index abd2d0cd6..7b4b6b777 100644 --- a/test/test_translate_song.cxx +++ b/test/test_translate_song.cxx @@ -207,7 +207,6 @@ TEST_F(TranslateSongTest, Insecure) TEST_F(TranslateSongTest, Secure) { DetachedSong song1(uri1, MakeTag1b()); - auto s1 = ToString(song1); auto se = ToString(DetachedSong(uri1, MakeTag1c())); const SongLoader loader(nullptr, nullptr); @@ -226,14 +225,12 @@ TEST_F(TranslateSongTest, InDatabase) loader)); DetachedSong song2(uri2, MakeTag2b()); - auto s1 = ToString(song2); auto se = ToString(DetachedSong(uri2, MakeTag2c())); EXPECT_TRUE(playlist_check_translate_song(song2, nullptr, loader)); EXPECT_EQ(se, ToString(song2)); DetachedSong song3("/music/foo/bar.ogg", MakeTag2b()); - s1 = ToString(song3); se = ToString(DetachedSong(uri2, MakeTag2c())); EXPECT_TRUE(playlist_check_translate_song(song3, nullptr, loader)); @@ -249,7 +246,6 @@ TEST_F(TranslateSongTest, Relative) /* map to music_directory */ DetachedSong song1("bar.ogg", MakeTag2b()); - auto s1 = ToString(song1); auto se = ToString(DetachedSong(uri2, MakeTag2c())); EXPECT_TRUE(playlist_check_translate_song(song1, "/music/foo", insecure_loader)); @@ -262,7 +258,6 @@ TEST_F(TranslateSongTest, Relative) /* legal because secure=true */ DetachedSong song3("bar.ogg", MakeTag1b()); - s1 = ToString(song3); se = ToString(DetachedSong(uri1, MakeTag1c())); EXPECT_TRUE(playlist_check_translate_song(song3, "/foo", secure_loader)); @@ -270,9 +265,28 @@ TEST_F(TranslateSongTest, Relative) /* relative to http:// */ DetachedSong song4("bar.ogg", MakeTag2a()); - s1 = ToString(song4); se = ToString(DetachedSong("http://example.com/foo/bar.ogg", MakeTag2a())); EXPECT_TRUE(playlist_check_translate_song(song4, "http://example.com/foo", insecure_loader)); EXPECT_EQ(se, ToString(song4)); } + +TEST_F(TranslateSongTest, Backslash) +{ + const SongLoader loader(reinterpret_cast(1), + storage); + + DetachedSong song1("foo\\bar.ogg", MakeTag2b()); +#ifdef _WIN32 + /* on Windows, all backslashes are converted to slashes in + relative paths from playlists */ + auto se = ToString(DetachedSong(uri2, MakeTag2c())); + EXPECT_TRUE(playlist_check_translate_song(song1, nullptr, + loader)); + EXPECT_EQ(se, ToString(song1)); +#else + /* backslash only supported on Windows */ + EXPECT_FALSE(playlist_check_translate_song(song1, nullptr, + loader)); +#endif +}