Skip to content

Commit 168df33

Browse files
authored
add play queue API & tests (#282)
1 parent f778ad7 commit 168df33

36 files changed

+861
-265
lines changed

cpp/server/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ set(
8989
json.hpp
9090
log.cpp log.hpp
9191
parsing.cpp parsing.hpp
92-
player_api.cpp player_api.hpp
92+
play_queue_controller.cpp
93+
play_queue_controller.hpp
94+
player_api.hpp
9395
player_api_json.cpp player_api_json.hpp
9496
player_api_parsers.cpp player_api_parsers.hpp
9597
player_controller.cpp player_controller.hpp

cpp/server/core_types.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ struct Range
2929
int32_t offset;
3030
int32_t count;
3131

32-
int32_t endOffset()
32+
int32_t endOffset() const
3333
{
3434
return offset + count;
3535
}

cpp/server/deadbeef/player.hpp

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,36 @@
1212

1313
namespace msrv::player_deadbeef {
1414

15-
class PlayerImpl : public Player
15+
class ColumnsQueryImpl final : public ColumnsQuery
16+
{
17+
public:
18+
explicit ColumnsQueryImpl(const std::vector<std::string>& columns)
19+
: columns_(compileColumns(columns))
20+
{
21+
}
22+
23+
~ColumnsQueryImpl() = default;
24+
25+
std::vector<std::string> evaluate(ddb_playlist_t* playlist, ddb_playItem_t* item)
26+
{
27+
return evaluateColumns(playlist, item, columns_);
28+
}
29+
30+
private:
31+
std::vector<TitleFormatPtr> columns_;
32+
};
33+
34+
class PlayerImpl final : public Player
1635
{
1736
public:
1837
static void endModifyPlaylist(ddb_playlist_t* playlist);
1938

2039
PlayerImpl();
21-
~PlayerImpl() override;
40+
~PlayerImpl() = default;
2241

2342
std::unique_ptr<WorkQueue> createWorkQueue() override;
2443

25-
PlayerStatePtr queryPlayerState(TrackQuery* activeItemQuery = nullptr) override;
44+
PlayerStatePtr queryPlayerState(ColumnsQuery* activeItemQuery = nullptr) override;
2645

2746
void playCurrent() override;
2847
void playItem(const PlaylistRef& playlist, int32_t itemIndex) override;
@@ -40,11 +59,10 @@ class PlayerImpl : public Player
4059
void seekRelative(double offsetSeconds) override;
4160
void setVolume(double val) override;
4261

43-
TrackQueryPtr createTrackQuery(
44-
const std::vector<std::string>& columns) override;
62+
ColumnsQueryPtr createColumnsQuery(const std::vector<std::string>& columns) override;
4563

4664
std::vector<PlaylistInfo> getPlaylists() override;
47-
PlaylistItemsResult getPlaylistItems(PlaylistQuery* query) override;
65+
PlaylistItemsResult getPlaylistItems(const PlaylistRef& plref, const Range& range, ColumnsQuery* query) override;
4866

4967
void addPlaylist(int32_t index, const std::string& title) override;
5068
void removePlaylist(const PlaylistRef& playlist) override;
@@ -82,10 +100,11 @@ class PlayerImpl : public Player
82100

83101
void sortPlaylistRandom(const PlaylistRef& plref) override;
84102

85-
PlaylistQueryPtr createPlaylistQuery(
86-
const PlaylistRef& playlist,
87-
const Range& range,
88-
const std::vector<std::string>& columns) override;
103+
std::vector<PlayQueueItemInfo> getPlayQueue(ColumnsQuery* query = nullptr) override;
104+
void addToPlayQueue(const PlaylistRef& plref, int32_t itemIndex, int32_t queueIndex) override;
105+
void removeFromPlayQueue(int32_t queueIndex) override;
106+
void removeFromPlayQueue(const PlaylistRef& plref, int32_t itemIndex) override;
107+
void clearPlayQueue() override;
89108

90109
boost::unique_future<ArtworkResult> fetchCurrentArtwork() override;
91110
boost::unique_future<ArtworkResult> fetchArtwork(const ArtworkQuery& query) override;
@@ -98,7 +117,7 @@ class PlayerImpl : public Player
98117
using PlaylistItemSelector = DB_playItem_t* (*)(DB_playItem_t*, int);
99118

100119
PlaybackState getPlaybackState(ddb_playItem_t* activeItem);
101-
void queryActiveItem(ActiveItemInfo* info, ddb_playItem_t* activeItem, TrackQuery* query);
120+
void queryActiveItem(ActiveItemInfo* info, ddb_playItem_t* activeItem, ColumnsQuery* query);
102121
void queryVolume(VolumeInfo* info);
103122
void queryInfo(PlayerInfo* info);
104123
void initVersion();

cpp/server/deadbeef/player_control.cpp

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,14 @@ namespace player_deadbeef {
77

88
namespace {
99

10-
class TrackQueryImpl : public TrackQuery
11-
{
12-
public:
13-
TrackQueryImpl(const std::vector<std::string>& columns)
14-
: formatters(compileColumns(columns))
15-
{
16-
}
17-
18-
~TrackQueryImpl() = default;
19-
20-
std::vector<TitleFormatPtr> formatters;
21-
};
22-
2310
inline float clampVolume(float val)
2411
{
2512
return std::max(std::min(val, 0.0f), ddbApi->volume_get_min_db());
2613
}
2714

2815
}
2916

30-
std::unique_ptr<TrackQuery> PlayerImpl::createTrackQuery(
31-
const std::vector<std::string>& columns)
32-
{
33-
return std::make_unique<TrackQueryImpl>(columns);
34-
}
35-
36-
std::unique_ptr<PlayerState> PlayerImpl::queryPlayerState(TrackQuery* activeItemQuery)
17+
std::unique_ptr<PlayerState> PlayerImpl::queryPlayerState(ColumnsQuery* activeItemQuery)
3718
{
3819
auto state = std::make_unique<PlayerState>();
3920

@@ -79,7 +60,7 @@ PlaybackState PlayerImpl::getPlaybackState(ddb_playItem_t* activeItem)
7960
}
8061
}
8162

82-
void PlayerImpl::queryActiveItem(ActiveItemInfo* info, ddb_playItem_t* activeItem, TrackQuery* query)
63+
void PlayerImpl::queryActiveItem(ActiveItemInfo* info, ddb_playItem_t* activeItem, ColumnsQuery* query)
8364
{
8465
int playlistIndex = ddbApi->streamer_get_current_playlist();
8566

@@ -102,12 +83,13 @@ void PlayerImpl::queryActiveItem(ActiveItemInfo* info, ddb_playItem_t* activeIte
10283
itemDuration = ddbApi->pl_get_item_duration(activeItem);
10384

10485
if (playlist)
86+
{
10587
itemIndex = ddbApi->plt_get_item_idx(playlist.get(), activeItem, PL_MAIN);
88+
}
10689

107-
if (query)
90+
if (auto queryImpl = dynamic_cast<ColumnsQueryImpl*>(query))
10891
{
109-
auto queryImpl = static_cast<TrackQueryImpl*>(query);
110-
columns = evaluateColumns(playlist.get(), activeItem, queryImpl->formatters);
92+
columns = queryImpl->evaluate(playlist.get(), activeItem);
11193
}
11294
}
11395

cpp/server/deadbeef/player_misc.cpp

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ PlayerImpl::PlayerImpl()
1212
addOption(&stopAfterCurrentAlbumOption_);
1313
}
1414

15-
PlayerImpl::~PlayerImpl() = default;
15+
ColumnsQueryPtr PlayerImpl::createColumnsQuery(const std::vector<std::string>& columns)
16+
{
17+
return std::make_unique<ColumnsQueryImpl>(columns);
18+
}
1619

1720
std::unique_ptr<WorkQueue> PlayerImpl::createWorkQueue()
1821
{
@@ -46,6 +49,88 @@ void PlayerImpl::disconnect()
4649
artworkFetcher_.reset();
4750
}
4851

52+
std::vector<PlayQueueItemInfo> PlayerImpl::getPlayQueue(ColumnsQuery* query)
53+
{
54+
auto queryImpl = dynamic_cast<ColumnsQueryImpl*>(query);
55+
56+
PlaylistLockGuard lock(playlistMutex_);
57+
58+
std::vector<PlayQueueItemInfo> items;
59+
auto size = ddbApi->playqueue_get_count();
60+
61+
if (!size)
62+
{
63+
return items;
64+
}
65+
66+
items.reserve(size);
67+
68+
for (auto i = 0; i < size; i++)
69+
{
70+
PlaylistItemPtr item(ddbApi->playqueue_get_item(i));
71+
PlaylistPtr playlist(ddbApi->pl_get_playlist(item.get()));
72+
auto playlistId = playlists_.getId(playlist.get());
73+
auto playlistIndex = ddbApi->plt_get_idx(playlist.get());
74+
auto itemIndex = ddbApi->plt_get_item_idx(playlist.get(), item.get(), PL_MAIN);
75+
76+
if (queryImpl)
77+
{
78+
auto columns = queryImpl->evaluate(playlist.get(), item.get());
79+
items.emplace_back(playlistId, playlistIndex, itemIndex, std::move(columns));
80+
}
81+
else
82+
{
83+
items.emplace_back(playlistId, playlistIndex, itemIndex);
84+
}
85+
}
86+
87+
return items;
88+
}
89+
90+
void PlayerImpl::addToPlayQueue(const PlaylistRef& plref, int32_t itemIndex, int32_t queueIndex)
91+
{
92+
PlaylistLockGuard lock(playlistMutex_);
93+
94+
auto playlist = playlists_.resolve(plref);
95+
auto item = resolvePlaylistItem(playlist.get(), itemIndex);
96+
97+
if (!item)
98+
throw InvalidRequestException("itemIndex is out of range");
99+
100+
if (queueIndex < 0 || queueIndex >= ddbApi->playqueue_get_count())
101+
ddbApi->playqueue_push(item.get());
102+
else
103+
ddbApi->playqueue_insert_at(queueIndex, item.get());
104+
}
105+
106+
void PlayerImpl::removeFromPlayQueue(int32_t queueIndex)
107+
{
108+
PlaylistLockGuard lock(playlistMutex_);
109+
110+
if (queueIndex < 0 || queueIndex >= ddbApi->playqueue_get_count())
111+
throw InvalidRequestException("queueIndex is out of range");
112+
113+
ddbApi->playqueue_remove_nth(queueIndex);
114+
}
115+
116+
void PlayerImpl::removeFromPlayQueue(const PlaylistRef& plref, int32_t itemIndex)
117+
{
118+
PlaylistLockGuard lock(playlistMutex_);
119+
120+
auto playlist = playlists_.resolve(plref);
121+
auto item = resolvePlaylistItem(playlist.get(), itemIndex);
122+
123+
if (!item)
124+
throw InvalidRequestException("itemIndex is out of range");
125+
126+
ddbApi->playqueue_remove(item.get());
127+
}
128+
129+
void PlayerImpl::clearPlayQueue()
130+
{
131+
ddbApi->playqueue_clear();
132+
}
133+
49134
boost::unique_future<ArtworkResult> PlayerImpl::fetchCurrentArtwork()
50135
{
51136
if (!artworkFetcher_)
@@ -99,12 +184,24 @@ void PlayerImpl::handleMessage(uint32_t id, uintptr_t, uint32_t p1, uint32_t)
99184
emitEvents(PlayerEvents::PLAYER_CHANGED);
100185
break;
101186

187+
case DB_EV_TRACKINFOCHANGED:
188+
switch (p1)
189+
{
190+
case DDB_PLAYLIST_CHANGE_PLAYQUEUE:
191+
emitEvents(PlayerEvents::PLAY_QUEUE_CHANGED);
192+
break;
193+
}
194+
break;
195+
102196
case DB_EV_PLAYLISTCHANGED:
103197
switch (p1)
104198
{
105199
case DDB_PLAYLIST_CHANGE_CONTENT:
106-
// Notify player change for the case when currently played item is reordered or removed
107-
emitEvents(PlayerEvents::PLAYER_CHANGED | PlayerEvents::PLAYLIST_ITEMS_CHANGED);
200+
// Notify player/queue change for the case when currently played/queued item is reordered or removed
201+
emitEvents(
202+
PlayerEvents::PLAYER_CHANGED |
203+
PlayerEvents::PLAYLIST_ITEMS_CHANGED |
204+
PlayerEvents::PLAY_QUEUE_CHANGED);
108205
break;
109206

110207
case DDB_PLAYLIST_CHANGE_CREATED:
@@ -114,8 +211,15 @@ void PlayerImpl::handleMessage(uint32_t id, uintptr_t, uint32_t p1, uint32_t)
114211

115212
case DDB_PLAYLIST_CHANGE_DELETED:
116213
case DDB_PLAYLIST_CHANGE_POSITION:
117-
// Reordering or removing playlists might change index of currently playing playlist
118-
emitEvents(PlayerEvents::PLAYER_CHANGED | PlayerEvents::PLAYLIST_SET_CHANGED);
214+
// Reordering or removing playlists might change playlist index of currently playing/queued item
215+
emitEvents(
216+
PlayerEvents::PLAYER_CHANGED |
217+
PlayerEvents::PLAYLIST_SET_CHANGED |
218+
PlayerEvents::PLAY_QUEUE_CHANGED);
219+
break;
220+
221+
case DDB_PLAYLIST_CHANGE_PLAYQUEUE:
222+
emitEvents(PlayerEvents::PLAY_QUEUE_CHANGED);
119223
break;
120224
}
121225

cpp/server/deadbeef/player_playlists.cpp

Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,6 @@
44
namespace msrv {
55
namespace player_deadbeef {
66

7-
class PlaylistQueryImpl : public PlaylistQuery
8-
{
9-
public:
10-
PlaylistQueryImpl(
11-
const PlaylistRef& plrefVal,
12-
const Range& rangeVal,
13-
const std::vector<std::string>& columns);
14-
15-
~PlaylistQueryImpl() override;
16-
17-
PlaylistRef plref;
18-
Range range;
19-
std::vector<TitleFormatPtr> formatters;
20-
};
21-
227
class AddItemsTask
238
{
249
public:
@@ -57,14 +42,6 @@ void PlayerImpl::endModifyPlaylist(ddb_playlist_t* playlist)
5742
ddbApi->sendmessage(DB_EV_PLAYLISTCHANGED, 0, DDB_PLAYLIST_CHANGE_CONTENT, 0);
5843
}
5944

60-
std::unique_ptr<PlaylistQuery> PlayerImpl::createPlaylistQuery(
61-
const PlaylistRef& plref,
62-
const Range& range,
63-
const std::vector<std::string>& columns)
64-
{
65-
return std::make_unique<PlaylistQueryImpl>(plref, range, columns);
66-
}
67-
6845
std::vector<PlaylistInfo> PlayerImpl::getPlaylists()
6946
{
7047
std::vector<PlaylistInfo> playlists;
@@ -104,19 +81,19 @@ std::vector<PlaylistInfo> PlayerImpl::getPlaylists()
10481
return playlists;
10582
}
10683

107-
PlaylistItemsResult PlayerImpl::getPlaylistItems(PlaylistQuery* query)
84+
PlaylistItemsResult PlayerImpl::getPlaylistItems(const PlaylistRef& plref, const Range& range, ColumnsQuery* query)
10885
{
109-
auto queryImpl = dynamic_cast<PlaylistQueryImpl*>(query);
86+
auto queryImpl = dynamic_cast<ColumnsQueryImpl*>(query);
11087
if (!queryImpl)
111-
throw std::logic_error("Expected PlaylistQueryImpl");
88+
throw std::logic_error("ColumnsQueryImpl is required");
11289

11390
PlaylistLockGuard lock(playlistMutex_);
11491

115-
PlaylistPtr playlist = playlists_.resolve(queryImpl->plref);
92+
PlaylistPtr playlist = playlists_.resolve(plref);
11693

11794
int32_t totalCount = ddbApi->plt_get_item_count(playlist.get(), PL_MAIN);
118-
int32_t offset = std::min(queryImpl->range.offset, totalCount);
119-
int32_t endOffset = std::min(queryImpl->range.endOffset(), totalCount);
95+
int32_t offset = std::min(range.offset, totalCount);
96+
int32_t endOffset = std::min(range.endOffset(), totalCount);
12097
int32_t count = endOffset - offset;
12198

12299
std::vector<PlaylistItemInfo> items;
@@ -130,8 +107,7 @@ PlaylistItemsResult PlayerImpl::getPlaylistItems(PlaylistQuery* query)
130107
while (item && count > 0)
131108
{
132109
PlaylistItemInfo itemInfo;
133-
itemInfo.columns = evaluateColumns(
134-
playlist.get(), item.get(), queryImpl->formatters);
110+
itemInfo.columns = queryImpl->evaluate(playlist.get(), item.get());
135111
items.emplace_back(std::move(itemInfo));
136112
item.reset(ddbApi->pl_get_next(item.get(), PL_MAIN));
137113
count--;
@@ -307,18 +283,6 @@ void PlayerImpl::sortPlaylistRandom(const PlaylistRef& plref)
307283
endModifyPlaylist(playlist.get());
308284
}
309285

310-
PlaylistQueryImpl::PlaylistQueryImpl(
311-
const PlaylistRef& plrefVal,
312-
const Range& rangeVal,
313-
const std::vector<std::string>& columns)
314-
: plref(plrefVal),
315-
range(rangeVal),
316-
formatters(compileColumns(columns))
317-
{
318-
}
319-
320-
PlaylistQueryImpl::~PlaylistQueryImpl() = default;
321-
322286
AddItemsTask::AddItemsTask(
323287
PlaylistMapping* playlists,
324288
const PlaylistRef& plref,

0 commit comments

Comments
 (0)