Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ ver 0.25 (not yet released)
* require Meson 1.2

ver 0.24.15 (not yet released)
* protocol
- fix crash on "sticker delete"
* playlist
- asx, rss, xspf: limit to 16 MB
- asx, pls, rss, xspf: limit to 16 MB
- cue: fix problem playing CUE tracks in music directory root
* player
- fix noise with replay gain and cross-fade

ver 0.24.14 (2026/08/13)
* storage
Expand Down
7 changes: 6 additions & 1 deletion src/command/StickerCommands.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,12 @@ class DomainHandler {
? sticker_database.Delete(sticker_type, validated_uri)
: sticker_database.DeleteValue(sticker_type, validated_uri, name);
if (!ret) {
response.FmtError(ACK_ERROR_NO_EXIST, "no such sticker: {:?}", name);
if (name == nullptr)
response.FmtError(ACK_ERROR_NO_EXIST,
"no stickers found: {:?}", uri);
else
response.FmtError(ACK_ERROR_NO_EXIST,
"no such sticker: {:?}", name);
return CommandResult::ERROR;
}

Expand Down
52 changes: 52 additions & 0 deletions src/input/LimitedInputStream.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project

#include "LimitedInputStream.hxx"

#include <cassert>
#include <stdexcept>

LimitedInputStream::LimitedInputStream(InputStreamPtr _input,
const offset_type max_size)
:ProxyInputStream(std::move(_input)), remaining(max_size)
{
ProxyInputStream::Update();
Check();
}

void
LimitedInputStream::CheckSize()
{
if (size_checked || !IsReady())
return;

if (input->KnownSize() && input->GetRest() > remaining)
throw std::runtime_error("Input stream is too large");

size_checked = true;
}

void
LimitedInputStream::Check()
{
ProxyInputStream::Check();
CheckSize();
}

std::size_t
LimitedInputStream::Read(std::unique_lock<Mutex> &lock,
std::span<std::byte> dest)
{
assert(!dest.empty());

if (remaining < dest.size())
dest = dest.first(static_cast<std::size_t>(remaining) + 1);

const std::size_t nbytes = input->Read(lock, dest);
if (nbytes > remaining)
throw std::runtime_error("Input stream is too large");

remaining -= nbytes;
CopyAttributes();
return nbytes;
}
25 changes: 25 additions & 0 deletions src/input/LimitedInputStream.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright The Music Player Daemon Project

#pragma once

#include "ProxyInputStream.hxx"

/**
* An #InputStream proxy which limits the amount of data that may be
* read from the underlying stream.
*/
class LimitedInputStream final : public ProxyInputStream {
offset_type remaining;
bool size_checked = false;

void CheckSize();

public:
LimitedInputStream(InputStreamPtr _input, offset_type max_size);

/* virtual methods from class InputStream */
void Check() override;
std::size_t Read(std::unique_lock<Mutex> &lock,
std::span<std::byte> dest) override;
};
1 change: 1 addition & 0 deletions src/input/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ input_basic = static_library(
'input_basic',
'AsyncInputStream.cxx',
'LastInputStream.cxx',
'LimitedInputStream.cxx',
'MemoryInputStream.cxx',
'ProxyInputStream.cxx',
'RewindInputStream.cxx',
Expand Down
13 changes: 11 additions & 2 deletions src/output/Source.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,22 @@ AudioOutputSource::FilterChunk(const MusicChunk &chunk)
case */
mix_ratio = 1.0f - mix_ratio;

/* use the post-replay-gain format for mixing;
the ReplayGainFilter may convert the sample
format (e.g. S16 to S24_P32 when software
volume is enabled) */
const SampleFormat cross_fade_format =
replay_gain_filter
? replay_gain_filter->GetOutAudioFormat().format
: in_audio_format.format;

void *dest = cross_fade_buffer.Get(other_data.size());
memcpy(dest, other_data.data(), other_data.size());
if (!pcm_mix(cross_fade_dither, dest, data.data(), data.size(),
in_audio_format.format,
cross_fade_format,
mix_ratio))
throw FmtRuntimeError("Cannot cross-fade format {}",
in_audio_format.format);
cross_fade_format);

data = {(const std::byte *)dest, other_data.size()};
}
Expand Down
8 changes: 7 additions & 1 deletion src/playlist/plugins/PlsPlaylistPlugin.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "PlsPlaylistPlugin.hxx"
#include "../PlaylistPlugin.hxx"
#include "../MemorySongEnumerator.hxx"
#include "input/LimitedInputStream.hxx"
#include "input/TextInputStream.hxx"
#include "input/InputStream.hxx"
#include "song/DetachedSong.hxx"
Expand All @@ -19,6 +20,9 @@

using std::string_view_literals::operator""sv;

/* PLS playlists are materialized before any songs are returned. */
static constexpr offset_type PLS_PLAYLIST_MAX_SIZE = 16 * 1024 * 1024;

static bool
FindPlaylistSection(TextInputStream &is)
{
Expand Down Expand Up @@ -133,7 +137,9 @@ ParsePls(TextInputStream &is, std::forward_list<DetachedSong> &songs)
static bool
ParsePls(InputStreamPtr &&is, std::forward_list<DetachedSong> &songs)
{
TextInputStream tis(std::move(is));
InputStreamPtr limited = std::make_unique<LimitedInputStream>
(std::move(is), PLS_PLAYLIST_MAX_SIZE);
TextInputStream tis(std::move(limited));
if (!ParsePls(tis, songs)) {
is = tis.StealInputStream();
return false;
Expand Down
Loading