GCC Code Coverage Report


Directory: ./
File: Player.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 375 402 93.3%
Functions: 59 65 90.8%
Branches: 323 597 54.1%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include "fpp-json.h"
16 #include "fpphttp.h" // drogon/HTTP helpers used here; no longer pulled transitively (see fpphttp_types.h)
17
18 #include <atomic>
19 #include <chrono>
20 #include <cstdlib>
21 #include <ctime>
22 #include <functional>
23 #include <string>
24 #include <vector>
25
26 #include "Events.h"
27 #include "common.h"
28 #include "log.h"
29 #include "commands/Commands.h"
30
31 #include "Player.h"
32
33 // Playlists retired by the main loop are destroyed HERE, on the main thread,
34 // never by whichever thread happened to drop the last snapshot: ~Playlist runs
35 // Cleanup(), which parks every entry on Playlist.cpp's PL_ENTRY_CLEANUPS — an
36 // unsynchronized list that only the main thread drains. So the shared_ptr
37 // deleter does not delete; it parks the raw pointer here, and
38 // Playlist::Process() calls DrainRetiredPlaylists() at exactly the point in the
39 // frame where the old drain ran `delete p`.
40 //
41 // Declared ahead of Player::INSTANCE so it is constructed first and therefore
42 // destroyed last — ~Player drains it during static destruction.
43 static std::mutex s_retiredPlaylistLock;
44 static std::vector<Playlist*> s_retiredPlaylists;
45
46 496 static void RetirePlaylist(Playlist* p) {
47
1/2
✓ Branch 0 taken 496 times.
✗ Branch 1 not taken.
496 std::lock_guard<std::mutex> lk(s_retiredPlaylistLock);
48
1/2
✓ Branch 0 taken 496 times.
✗ Branch 1 not taken.
496 s_retiredPlaylists.push_back(p);
49 496 }
50
51 Player Player::INSTANCE;
52
53 // A count: a command holds one around StartPlaylist(), whose PlayImpl() holds
54 // its own.
55 static std::atomic<int> s_switchesInFlight{ 0 };
56 // When the count last left 0. A command's scope also covers the presets and
57 // plugin callbacks its stop and start fire, and a script plugin's callback is
58 // waited for with no timeout: past this long, the switch no longer holds the
59 // scheduler back.
60 static std::atomic<int64_t> s_switchSinceMs{ 0 };
61 static std::atomic<bool> s_switchOverdueLogged{ false };
62 static constexpr int64_t MAX_SWITCH_MS = 5000;
63
64 3999 static int64_t SteadyMs() {
65
1/2
✓ Branch 0 taken 3999 times.
✗ Branch 1 not taken.
3999 return std::chrono::duration_cast<std::chrono::milliseconds>(
66 7998 std::chrono::steady_clock::now().time_since_epoch())
67 7998 .count();
68 }
69
70 2835 Player::SwitchScope::SwitchScope() {
71
2/2
✓ Branch 0 taken 1542 times.
✓ Branch 1 taken 1293 times.
2835 if (s_switchesInFlight++ == 0) {
72 1542 s_switchSinceMs = SteadyMs();
73 1542 s_switchOverdueLogged = false;
74 }
75 2835 }
76
77 2835 Player::SwitchScope::~SwitchScope() {
78 2835 s_switchesInFlight--;
79 2835 }
80
81 5804 bool Player::Switching() {
82
2/2
✓ Branch 0 taken 3347 times.
✓ Branch 1 taken 2457 times.
5804 if (s_switchesInFlight == 0)
83 3347 return false;
84 2457 int64_t ms = SteadyMs() - s_switchSinceMs;
85
2/2
✓ Branch 0 taken 2452 times.
✓ Branch 1 taken 5 times.
2457 if (ms < MAX_SWITCH_MS)
86 2452 return true;
87
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (!s_switchOverdueLogged.exchange(true))
88 1 LogWarn(VB_PLAYLIST, "A playlist switch is still running after %lld ms (a slow PLAYLIST_STOPPED/STARTED preset or plugin callback?); the scheduler carries on\n", (long long)ms);
89 5 return false;
90 }
91
92 2346 Player::Player() :
93 2346 lastCheckTime(std::time(nullptr)),
94 2346 origStartTime(0),
95 2346 origStopTime(0),
96 2346 startTime(0),
97 2346 stopTime(0),
98 2346 stopMethod(0),
99 2346 priority(1000),
100 2346 allowScheduleOverride(true),
101 4692 forceStopped(false) {
102 2346 }
103
104 458 Player::~Player() {
105 // Static destruction: nothing will call Process() again, so drop the owning
106 // references and run the deferred deletes inline. The entries those
107 // destructors park on PL_ENTRY_CLEANUPS are left for the process to
108 // reclaim, exactly as the old `delete playlist` did.
109 458 m_playlist.store(nullptr);
110 458 std::vector<std::shared_ptr<Playlist>> owned;
111 {
112 458 std::lock_guard<std::mutex> lk(m_ownedPlaylistsLock);
113 458 owned.swap(m_ownedPlaylists);
114 458 }
115 458 owned.clear();
116 458 DrainRetiredPlaylists();
117 458 }
118
119 2385 std::shared_ptr<Playlist> Player::CreatePlaylist(std::shared_ptr<Playlist> parent) {
120
4/10
✓ Branch 0 taken 2385 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2385 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2385 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 2385 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2385 std::shared_ptr<Playlist> pl(new Playlist(std::move(parent)), RetirePlaylist);
121
1/2
✓ Branch 0 taken 2385 times.
✗ Branch 1 not taken.
2385 std::lock_guard<std::mutex> lk(m_ownedPlaylistsLock);
122
1/2
✓ Branch 0 taken 2385 times.
✗ Branch 1 not taken.
2385 m_ownedPlaylists.push_back(pl);
123 2385 return pl;
124 2385 }
125
126 2734 void Player::SetCurrentPlaylist(Playlist* p) {
127 2734 std::shared_ptr<Playlist> owned;
128 {
129
1/2
✓ Branch 0 taken 2734 times.
✗ Branch 1 not taken.
2734 std::lock_guard<std::mutex> lk(m_ownedPlaylistsLock);
130
1/2
✓ Branch 0 taken 3470 times.
✗ Branch 1 not taken.
3470 for (auto& sp : m_ownedPlaylists) {
131
2/2
✓ Branch 0 taken 2734 times.
✓ Branch 1 taken 736 times.
3470 if (sp.get() == p) {
132 2734 owned = sp;
133 2734 break;
134 }
135 }
136 2734 }
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2734 times.
2734 if (!owned) {
138 // Invariant: only an instance this player created and has not yet
139 // released can become current. Getting here means a raw Playlist*
140 // outlived its ownership — publishing it (or publishing null) would
141 // hand every reader a dangling or absent playlist, so keep the current
142 // one and make the breakage visible instead.
143 ✗ LogErr(VB_PLAYLIST, "Player::SetCurrentPlaylist() called with an unowned Playlist %p — keeping the current playlist\n",
144 (void*)p);
145 ✗ return;
146 }
147
1/2
✓ Branch 0 taken 2734 times.
✗ Branch 1 not taken.
2734 m_playlist.store(std::move(owned));
148
1/2
✓ Branch 0 taken 2734 times.
✗ Branch 1 not taken.
2734 }
149
150 497 void Player::ReleasePlaylist(Playlist* p) {
151 // Declared before the lock so the reference is dropped — and the deleter
152 // run, if this was the last one — after m_ownedPlaylistsLock is released.
153 497 std::shared_ptr<Playlist> dropped;
154 {
155
1/2
✓ Branch 0 taken 497 times.
✗ Branch 1 not taken.
497 std::lock_guard<std::mutex> lk(m_ownedPlaylistsLock);
156
1/2
✓ Branch 0 taken 1045 times.
✗ Branch 1 not taken.
1045 for (auto it = m_ownedPlaylists.begin(); it != m_ownedPlaylists.end(); ++it) {
157
2/2
✓ Branch 0 taken 497 times.
✓ Branch 1 taken 548 times.
1045 if (it->get() == p) {
158 497 dropped = std::move(*it);
159
1/2
✓ Branch 0 taken 497 times.
✗ Branch 1 not taken.
497 m_ownedPlaylists.erase(it);
160 497 break;
161 }
162 }
163 497 }
164 497 }
165
166 446801 void Player::DrainRetiredPlaylists() {
167 while (true) {
168 446801 Playlist* p = nullptr;
169 {
170
1/2
✓ Branch 0 taken 446801 times.
✗ Branch 1 not taken.
446801 std::lock_guard<std::mutex> lk(s_retiredPlaylistLock);
171
2/2
✓ Branch 0 taken 446305 times.
✓ Branch 1 taken 496 times.
446801 if (s_retiredPlaylists.empty())
172 892610 return;
173 496 p = s_retiredPlaylists.back();
174 496 s_retiredPlaylists.pop_back();
175
2/2
✓ Branch 0 taken 496 times.
✓ Branch 1 taken 446305 times.
446801 }
176 // Deliberately unlocked: ~Playlist runs arbitrary entry destructors.
177
1/2
✓ Branch 0 taken 496 times.
✗ Branch 1 not taken.
496 delete p;
178 496 }
179 }
180
181 1887 void Player::Init() {
182 /*
183 * Start Playlist Callback
184 */
185 std::function<void(const std::string&, const std::string&)>
186 1887 playlist_callback = [](const std::string& topic_in,
187 const std::string& payload) {
188 116 std::string emptyStr;
189
1/2
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
116 std::string topic = topic_in;
190
1/2
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
116 topic.replace(0, 14, emptyStr); // Replace until /#
191
192 116 int pos = topic.find("/");
193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
116 if (pos == std::string::npos) {
194 ✗ LogWarn(VB_PLAYLIST, "Ignoring Invalid playlist topic: playlist/%s\n",
195 topic.c_str());
196 ✗ return;
197 }
198
199
1/2
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
116 std::string newPlaylistName = topic.substr(0, pos);
200
1/2
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
116 std::string topicEnd = topic.substr(pos);
201
202
3/4
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
✓ Branch 3 taken 58 times.
116 if (topicEnd == "/start") {
203 58 pos = 0;
204
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 10 times.
58 if (!payload.empty()) {
205 48 pos = std::atoi(payload.c_str());
206 }
207
208
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 LogDebug(VB_CONTROL, "Starting Playlist '%s' with message '%s'\n",
209 newPlaylistName.c_str(), payload.c_str());
210 // Stop the running playlist first, as Start Playlist does.
211 // Stopped inside the start instead, its PLAYLIST_STOPPED
212 // preset and plugin "stop" callback are deferred and run
213 // after the new playlist has started, undoing its lead-in.
214
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 Player::SwitchScope switching;
215
3/4
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 22 times.
58 if (Player::INSTANCE.GetStatus() != FPP_STATUS_IDLE) {
216
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 Player::INSTANCE.StopNow(1);
217 }
218
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 Player::INSTANCE.StartPlaylist(newPlaylistName, -1, pos);
219
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 LogDebug(VB_CONTROL, "Call to Player::INSTANCE.StartPlaylist complete\n");
220 58 } else {
221 // Runs on the MQTT thread; the snapshot pins the instance for
222 // the call.
223
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 std::shared_ptr<Playlist> pl = Player::INSTANCE.PlaylistSnapshot();
224
1/2
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
58 if (pl)
225
3/6
✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 56 times.
✗ Branch 5 not taken.
58 pl->MQTTHandler(topic, payload);
226 56 };
227
228
1/2
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
114 LogDebug(VB_CONTROL, "exit playlist_callback (MQTT)\n");
229
2/4
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
2001 };
230
2/4
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1887 times.
✗ Branch 3 not taken.
1887 Events::AddCallback("/set/playlist/#", playlist_callback);
231
232
3/4
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 1851 times.
1887 if (Events::HasEventHandlers()) {
233 // Deprecated topics. Registered here, once, rather than per Playlist
234 // instance: a per-instance callback captured that instance, outlived
235 // it, and was re-added (growing the handler's topic list) by every
236 // inserted playlist. Same snapshot dispatch as /set/playlist/#.
237 std::function<void(const std::string&, const std::string&)> legacy_callback =
238 36 [](const std::string& t, const std::string& payload) {
239
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 LogDebug(VB_CONTROL, "Received deprecated MQTT Topic: '%s' \n", t.c_str());
240
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 std::string topic = t;
241
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 topic.replace(0, 10, ""); // strip "/playlist/"
242
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 std::shared_ptr<Playlist> pl = Player::INSTANCE.PlaylistSnapshot();
243
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 if (pl)
244
3/6
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 84 times.
✗ Branch 5 not taken.
84 pl->MQTTHandler(topic, payload);
245 120 };
246
2/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
72 Events::AddCallback("/playlist/name/set", legacy_callback);
247
2/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
72 Events::AddCallback("/playlist/repeat/set", legacy_callback);
248
2/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
36 Events::AddCallback("/playlist/sectionPosition/set", legacy_callback);
249 36 }
250
251 // The resident playlist. Created here rather than in the constructor
252 // because Playlist's own constructor reaches for Events, PluginManager and
253 // CommandManager, which are not yet built during static initialization.
254 // From this point on PlaylistSnapshot() never returns null: the resident
255 // instance is only ever replaced (SwitchToInsertedPlaylist, SetIdle's walk
256 // back up the parent chain), never cleared.
257
1/2
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
1887 std::shared_ptr<Playlist> pl = CreatePlaylist(nullptr);
258
1/2
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
1887 SetCurrentPlaylist(pl.get());
259 1887 }
260
261 1070 int Player::StartPlaylist(const std::string& name, const int repeat,
262 const int startPosition, const int endPosition, const int manualPriority,
263 const bool allowScheduleOverride) {
264 {
265
1/2
✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
1070 std::lock_guard<std::mutex> lk(m_stateLock);
266
5/6
✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
✓ Branch 3 taken 1015 times.
✓ Branch 4 taken 1017 times.
✓ Branch 5 taken 53 times.
1125 if ((GetStatus() == FPP_STATUS_IDLE) ||
267
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 53 times.
55 (name != playlistName)) {
268
1/2
✓ Branch 0 taken 1017 times.
✗ Branch 1 not taken.
1017 playlistName = name;
269 1017 startTime = std::time(nullptr);
270 1017 stopTime = 0;
271 1017 origStopTime = 0;
272 1017 stopMethod = 0;
273 1017 priority = manualPriority;
274 1017 this->allowScheduleOverride = allowScheduleOverride;
275 }
276
277 1070 forceStopped = false;
278
1/2
✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
1070 forceStoppedPlaylist = "";
279 1070 }
280
281
1/2
✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
1070 std::shared_ptr<Playlist> pl = PlaylistSnapshot();
282
5/6
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 1012 times.
✓ Branch 2 taken 253 times.
✓ Branch 3 taken 817 times.
✓ Branch 4 taken 1070 times.
✗ Branch 5 not taken.
1070 LogDebug(VB_PLAYLIST, "Manually starting %srepeating playlist '%s'\n",
283 (repeat == -1 ? pl->GetRepeat() : repeat) ? "" : "non-",
284 name.c_str());
285
286
1/2
✓ Branch 0 taken 1070 times.
✗ Branch 1 not taken.
2140 return pl->Play(name, startPosition, repeat, -1, endPosition);
287 1070 }
288
289 89 int Player::StartScheduledPlaylist(const std::string& name, const int position,
290 const int repeat, const int scheduleEntry, const int scheduledPriority,
291 const time_t sTime, const time_t eTime, const int method) {
292 {
293
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 std::lock_guard<std::mutex> lk(m_stateLock);
294
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 playlistName = name;
295 89 origStartTime = sTime;
296 89 origStopTime = eTime;
297 89 startTime = std::time(nullptr);
298 89 stopTime = eTime;
299 89 stopMethod = method;
300 89 priority = scheduledPriority;
301 89 forceStopped = false;
302
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 forceStoppedPlaylist = "";
303 89 }
304
305
7/8
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 77 times.
✓ Branch 7 taken 12 times.
89 LogDebug(VB_PLAYLIST, "Starting %srepeating playlist '%s' with scheduled '%s' in %d seconds\n",
306 repeat ? "" : "non-",
307 name.c_str(),
308 method == 0 ? "Graceful Stop" : method == 1 ? "Hard Stop"
309 : method == 2 ? "Graceful Stop After Loop"
310 : "",
311 (int)(eTime - std::time(nullptr)));
312
313
2/4
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 89 times.
✗ Branch 3 not taken.
89 return PlaylistSnapshot()->Play(name, position, repeat, scheduleEntry);
314 }
315
316 3 int Player::AdjustPlaylistStopTime(const int seconds) {
317
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if ((GetStatus() != FPP_STATUS_PLAYLIST_PLAYING) &&
318
2/8
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
3 (GetStatus() != FPP_STATUS_STOPPING_GRACEFULLY) &&
319 ✗ (GetStatus() != FPP_STATUS_STOPPING_GRACEFULLY_AFTER_LOOP)) {
320 ✗ LogInfo(VB_SCHEDULE, "Tried to extend a running playlist, but there is no playlist running.\n");
321 ✗ return 0;
322 }
323
324
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if (!WasScheduled()) {
325 ✗ LogInfo(VB_SCHEDULE, "Tried to extend running playlist, but it was manually started.\n");
326 ✗ return 0;
327 }
328
329 // UI should catch this, but also check here
330
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
3 if ((seconds > (12 * 60 * 60)) ||
331 (seconds < (-3 * 60 * 60))) {
332 ✗ return 0;
333 }
334
335
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 std::lock_guard<std::mutex> lk(m_stateLock);
336
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (seconds >= 0) {
337
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogDebug(VB_PLAYLIST, "Extending scheduled playlist '%s' by %d seconds\n",
338 playlistName.c_str(), seconds);
339 } else {
340
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogDebug(VB_PLAYLIST, "Shortening scheduled playlist '%s' by %d seconds\n",
341 playlistName.c_str(), seconds);
342 }
343
344 3 stopTime += seconds;
345
346 char timeBuf[32];
347
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 LogDebug(VB_PLAYLIST, "New end time is now: %s", ctime_r(&stopTime, timeBuf));
348
349 3 return 1;
350 3 }
351
352 9 void Player::InsertPlaylistAsNext(const std::string& filename, const int startPosition, const int endPos) {
353
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 PlaylistSnapshot()->InsertPlaylistAsNext(filename, startPosition, endPos);
354 9 }
355
356 582 void Player::InsertPlaylistImmediate(const std::string& filename, const int startPosition, const int endPos) {
357
2/4
✓ Branch 0 taken 582 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 582 times.
✗ Branch 3 not taken.
582 PlaylistSnapshot()->InsertPlaylistImmediate(filename, startPosition, endPos);
358 582 }
359
360 812 void Player::RecordForceStop(int forceStop) {
361
1/2
✓ Branch 0 taken 812 times.
✗ Branch 1 not taken.
812 std::lock_guard<std::mutex> lk(m_stateLock);
362 812 forceStopped = (bool)forceStop;
363
2/2
✓ Branch 0 taken 802 times.
✓ Branch 1 taken 10 times.
812 if (forceStopped)
364
1/2
✓ Branch 0 taken 802 times.
✗ Branch 1 not taken.
802 forceStoppedPlaylist = playlistName;
365 else
366
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 forceStoppedPlaylist = "";
367 812 }
368
369 801 int Player::StopNow(int forceStop) {
370
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 800 times.
801 if (InHandedBackStoppedPreset()) {
371 1 LogInfo(VB_PLAYLIST, "Ignoring Stop Now from the PLAYLIST_STOPPED preset of an inserted playlist: it has already stopped, and the playlist it interrupted carries on\n");
372 1 return 1;
373 }
374 800 RecordForceStop(forceStop);
375
2/4
✓ Branch 0 taken 800 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 798 times.
✗ Branch 3 not taken.
800 return PlaylistSnapshot()->StopNow(forceStop);
376 }
377
378 12 int Player::StopGracefully(int forceStop, int afterCurrentLoop) {
379
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (InHandedBackStoppedPreset()) {
380 ✗ LogInfo(VB_PLAYLIST, "Ignoring Stop Gracefully from the PLAYLIST_STOPPED preset of an inserted playlist: it has already stopped, and the playlist it interrupted carries on\n");
381 ✗ return 1;
382 }
383 12 RecordForceStop(forceStop);
384
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 return PlaylistSnapshot()->StopGracefully(forceStop, afterCurrentLoop);
385 }
386
387 7 void Player::StopNowAllLevels(int forceStop) {
388
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 int levels = PlaylistSnapshot()->GetLevelCount();
389
5/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 7 times.
14 for (int i = 0; i < levels && GetStatus() != FPP_STATUS_IDLE; i++) {
390 7 StopNow(forceStop);
391 }
392 7 }
393
394 4622 std::time_t Player::GetOrigStartTime() {
395
1/2
✓ Branch 0 taken 4622 times.
✗ Branch 1 not taken.
4622 std::lock_guard<std::mutex> lk(m_stateLock);
396 4622 return origStartTime;
397 4622 }
398 2454 std::time_t Player::GetOrigStopTime() {
399
1/2
✓ Branch 0 taken 2454 times.
✗ Branch 1 not taken.
2454 std::lock_guard<std::mutex> lk(m_stateLock);
400 2454 return origStopTime;
401 2454 }
402 2456 std::time_t Player::GetStartTime() {
403
1/2
✓ Branch 0 taken 2456 times.
✗ Branch 1 not taken.
2456 std::lock_guard<std::mutex> lk(m_stateLock);
404 2456 return startTime;
405 2456 }
406 4582 std::time_t Player::GetStopTime() {
407
1/2
✓ Branch 0 taken 4582 times.
✗ Branch 1 not taken.
4582 std::lock_guard<std::mutex> lk(m_stateLock);
408 4582 return stopTime;
409 4582 }
410 117 void Player::ClearForceStopped() {
411
1/2
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
117 std::lock_guard<std::mutex> lk(m_stateLock);
412 117 forceStopped = false;
413 117 }
414 858 bool Player::GetForceStopped() {
415
1/2
✓ Branch 0 taken 858 times.
✗ Branch 1 not taken.
858 std::lock_guard<std::mutex> lk(m_stateLock);
416 858 return forceStopped;
417 858 }
418 40 std::string Player::GetForceStoppedPlaylist() {
419
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 std::lock_guard<std::mutex> lk(m_stateLock);
420
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
80 return forceStoppedPlaylist;
421 40 }
422 2268 int Player::GetPriority() {
423
1/2
✓ Branch 0 taken 2268 times.
✗ Branch 1 not taken.
2268 std::lock_guard<std::mutex> lk(m_stateLock);
424 2268 return priority;
425 2268 }
426 8 bool Player::GetAllowScheduleOverride() {
427
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 std::lock_guard<std::mutex> lk(m_stateLock);
428 8 return allowScheduleOverride;
429 8 }
430
431 298513 int Player::Process() {
432 298513 std::time_t procTime = std::time(nullptr);
433
2/4
✓ Branch 0 taken 298513 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 298513 times.
✗ Branch 3 not taken.
298513 PlaylistStatus status = PlaylistSnapshot()->getPlaylistStatus();
434
435 // See if we need to stop a scheduled playlist. A paused one counts: it
436 // used to be skipped, so a playlist paused during its window never
437 // stopped, and as the scheduler waits for idle, every later scheduled show
438 // was skipped too.
439 298513 bool check = false;
440 298513 std::time_t stop = 0;
441 298513 std::time_t origStop = 0;
442 298513 int method = 0;
443 298513 std::string name;
444 {
445
1/2
✓ Branch 0 taken 298513 times.
✗ Branch 1 not taken.
298513 std::lock_guard<std::mutex> lk(m_stateLock);
446
4/4
✓ Branch 0 taken 8286 times.
✓ Branch 1 taken 290227 times.
✓ Branch 2 taken 3311 times.
✓ Branch 3 taken 4975 times.
298513 if (((status == FPP_STATUS_PLAYLIST_PLAYING) || (status == FPP_STATUS_PLAYLIST_PAUSED)) &&
447
2/2
✓ Branch 0 taken 31791 times.
✓ Branch 1 taken 261747 times.
293538 (stopTime) &&
448
2/2
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 31441 times.
31791 (lastCheckTime != procTime)) {
449 350 lastCheckTime = procTime;
450 350 check = true;
451 350 stop = stopTime;
452 350 origStop = origStopTime;
453 350 method = stopMethod;
454
1/2
✓ Branch 0 taken 350 times.
✗ Branch 1 not taken.
350 name = playlistName;
455 }
456 298513 }
457
458
2/2
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 298163 times.
298513 if (check) {
459
2/2
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 9 times.
350 if (stop > procTime) {
460 341 bool logSwitch = false;
461 341 int diff = stop - procTime;
462
463 // Print the countdown more frequently as we get closer
464
6/6
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 201 times.
✓ Branch 2 taken 138 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 143 times.
✓ Branch 5 taken 196 times.
341 if (((diff > 300) && ((diff % 300) == 0)) ||
465
5/6
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 254 times.
✓ Branch 5 taken 85 times.
339 ((diff > 60) && (diff <= 300) && ((diff % 60) == 0)) ||
466
6/6
✓ Branch 0 taken 111 times.
✓ Branch 1 taken 143 times.
✓ Branch 2 taken 105 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 85 times.
✓ Branch 5 taken 248 times.
339 ((diff > 10) && (diff <= 60) && ((diff % 10) == 0)) ||
467 ((diff <= 10))) {
468 93 logSwitch = true;
469 }
470
471
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 248 times.
341 if (logSwitch) {
472
8/10
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 61 times.
✓ Branch 3 taken 32 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 49 times.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 93 times.
✗ Branch 9 not taken.
93 LogDebug(VB_PLAYLIST, "Playlist '%s' will switch to '%s' in %d second%s\n",
473 name.c_str(),
474 method == 0 ? "Stopping Gracefully" : method == 1 ? "Hard Stop"
475 : method == 2 ? "Stopping Gracefully After Loop"
476 : "",
477 diff,
478 diff == 1 ? "" : "s");
479 }
480 }
481
482
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 341 times.
350 if (stop <= procTime) {
483 // if user shortened the scheduled end time mark as force stopped
484
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 int forceStop = (stop < origStop) ? 1 : 0;
485
486 // A paused playlist just stops at the end of its window: a
487 // graceful stop would first resume it, bringing the show back on
488 // after its time to finish the interrupted entry.
489
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 if (status == FPP_STATUS_PLAYLIST_PAUSED)
490 1 method = 1;
491
492
3/3
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
9 switch (method) {
493 2 case 0: // Gracefully
494
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 StopGracefully(forceStop);
495 2 break;
496 1 case 2: // After Loop
497
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 StopGracefully(forceStop, 1);
498 1 break;
499 6 case 1: // Hard Stop
500 default:
501
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 StopNowAllLevels(forceStop);
502 6 break;
503 }
504 }
505 }
506
507 // Deliberately a second snapshot rather than one hoisted to the top of the
508 // method: the stop handling above can legitimately change which playlist is
509 // current (a hard stop of an inserted playlist unwinds one level and hands
510 // the player back to the parent), and this tick must process whichever one
511 // is current now — which is what the raw global did.
512
2/4
✓ Branch 0 taken 298513 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 298513 times.
✗ Branch 3 not taken.
597026 return PlaylistSnapshot()->Process();
513 298513 }
514
515 11389 void Player::ProcessMedia() {
516
2/4
✓ Branch 0 taken 11389 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11389 times.
✗ Branch 3 not taken.
11389 PlaylistSnapshot()->ProcessMedia();
517 11389 }
518
519 1383000 int Player::IsPlaying() {
520
2/4
✓ Branch 0 taken 1383000 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1383000 times.
✗ Branch 3 not taken.
1383000 return PlaylistSnapshot()->IsPlaying();
521 }
522
523 391060 std::string Player::GetPlaylistName() {
524
2/4
✓ Branch 0 taken 391060 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 391060 times.
✗ Branch 3 not taken.
391060 return PlaylistSnapshot()->GetPlaylistName();
525 }
526
527 1421443 PlaylistStatus Player::GetStatus() {
528
2/4
✓ Branch 0 taken 1421443 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1421443 times.
✗ Branch 3 not taken.
1421443 return PlaylistSnapshot()->getPlaylistStatus();
529 }
530
531 290239 int Player::GetRepeat() {
532
1/2
✓ Branch 0 taken 290239 times.
✗ Branch 1 not taken.
290239 return PlaylistSnapshot()->GetRepeat();
533 }
534
535 3 void Player::SetRepeat(int repeat) {
536
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 PlaylistSnapshot()->SetRepeat(repeat);
537 3 }
538
539 2454 int Player::GetStopMethod() {
540
1/2
✓ Branch 0 taken 2454 times.
✗ Branch 1 not taken.
2454 std::lock_guard<std::mutex> lk(m_stateLock);
541 2454 return stopMethod;
542 2454 }
543
544 290232 int Player::GetPosition() {
545
2/4
✓ Branch 0 taken 290232 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 290232 times.
✗ Branch 3 not taken.
290232 return PlaylistSnapshot()->GetPosition();
546 }
547
548 6 int Player::GetResumePosition() {
549
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 return PlaylistSnapshot()->GetResumePosition();
550 }
551
552 25 Json::Value Player::GetInfo(void) {
553
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
25 return PlaylistSnapshot()->GetInfo();
554 }
555
556 2484 int Player::GetScheduleEntry() {
557
1/2
✓ Branch 0 taken 2484 times.
✗ Branch 1 not taken.
2484 return PlaylistSnapshot()->GetScheduleEntry();
558 }
559
560 5 uint64_t Player::GetFileTime() {
561
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 return PlaylistSnapshot()->GetFileTime();
562 }
563
564 6 Json::Value Player::GetConfig() {
565
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 return PlaylistSnapshot()->GetConfig();
566 }
567
568 4 Json::Value Player::GetMqttStatusJSON() {
569
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 return PlaylistSnapshot()->GetMqttStatusJSON();
570 }
571
572 490381 int Player::WasScheduled() {
573
2/4
✓ Branch 0 taken 490381 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 490381 times.
✗ Branch 3 not taken.
490381 return PlaylistSnapshot()->WasScheduled();
574 }
575
576 ✗ int Player::FindPosForMS(uint64_t& ms, bool itemDefinedOnly) {
577 ✗ return PlaylistSnapshot()->FindPosForMS(ms, itemDefinedOnly);
578 }
579
580 2 void Player::GetFilenamesForPos(int pos, std::string& seq, std::string& med) {
581
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 PlaylistSnapshot()->GetFilenamesForPos(pos, seq, med);
582 2 }
583
584 2 int Player::Load(const std::string filename) {
585
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
6 return PlaylistSnapshot()->Load(filename.c_str());
586 }
587
588 ✗ int Player::Start() {
589 ✗ return PlaylistSnapshot()->Start();
590 }
591
592 9050 void Player::RestartItem() {
593
2/4
✓ Branch 0 taken 9050 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9050 times.
✗ Branch 3 not taken.
9050 PlaylistSnapshot()->RestartItem();
594 9050 }
595
596 9112 void Player::NextItem() {
597
2/4
✓ Branch 0 taken 9112 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9112 times.
✗ Branch 3 not taken.
9112 PlaylistSnapshot()->NextItem();
598 9112 }
599
600 9080 void Player::PrevItem() {
601
2/4
✓ Branch 0 taken 9080 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9080 times.
✗ Branch 3 not taken.
9080 PlaylistSnapshot()->PrevItem();
602 9080 }
603
604 15 void Player::Pause() {
605
2/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 PlaylistSnapshot()->Pause();
606 15 }
607
608 13 void Player::Resume() {
609
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
13 PlaylistSnapshot()->Resume();
610 13 }
611
612 560 int Player::Cleanup(void) {
613 // The main loop calls this after an unlocked "now idle" read. A start from
614 // another thread can land between that read and here, so only clean up a
615 // playlist that is still idle under its lock; SetIdle() has already run
616 // Cleanup() on every transition to idle.
617
2/4
✓ Branch 0 taken 560 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 560 times.
✗ Branch 3 not taken.
560 return PlaylistSnapshot()->CleanupIfIdle();
618 }
619
620 238337 void Player::GetCurrentStatus(Json::Value& result) {
621
2/4
✓ Branch 0 taken 238337 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 238337 times.
✗ Branch 3 not taken.
238337 PlaylistSnapshot()->GetCurrentStatus(result);
622 238337 }
623
624 25358 Json::Value Player::GetStatusJSON() {
625
1/2
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
25358 Json::Value result;
626
1/2
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
25358 Json::Value playlists(Json::arrayValue);
627
1/2
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
25358 Json::Value pl;
628
629 // One snapshot for the whole object: six separate loads could straddle a
630 // playlist switch and report a mix of two playlists in one status blob.
631
1/2
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
25358 std::shared_ptr<Playlist> cur = PlaylistSnapshot();
632
1/2
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
25358 pl = cur->GetInfo();
633
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 pl["details"] = cur->GetConfig();
634
3/6
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25358 times.
✗ Branch 5 not taken.
25358 pl["status"] = (int)cur->getPlaylistStatus();
635
3/6
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25358 times.
✗ Branch 5 not taken.
25358 pl["scheduled"] = cur->WasScheduled() ? true : false;
636
3/6
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25358 times.
✗ Branch 5 not taken.
25358 pl["position"] = cur->GetPosition();
637
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 pl["lastModified"] = (Json::UInt64)cur->GetFileTime();
638
639 // things we store locally that would need to be in an array if we can
640 // play multiple playlists concurrently
641
1/2
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
25358 std::lock_guard<std::mutex> lk(m_stateLock);
642
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 pl["origStartTime"] = (Json::UInt64)origStartTime;
643
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 pl["origStopTime"] = (Json::UInt64)origStopTime;
644
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 pl["startTime"] = (Json::UInt64)startTime;
645
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 pl["stopTime"] = (Json::UInt64)stopTime;
646
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 pl["stopMethod"] = stopMethod;
647
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 pl["priority"] = priority;
648
649
1/2
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
25358 playlists.append(pl);
650
651
2/4
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
25358 result["playlists"] = playlists;
652
653 25358 return result;
654 25358 }
655
656 // --------------------------------------------------------------------------
657 // OpenAPI docs for the /player/* endpoints handled below.
658 // --------------------------------------------------------------------------
659
660 /**
661 * Get the player status. Equivalent to /api/player/status.
662 *
663 * @route GET /api/player
664 * @response 200 Player status JSON.
665 */
666
667 /**
668 * Get the player status (playlist, sequence, timing, mode, etc.).
669 *
670 * @route GET /api/player/status
671 * @response 200 Player status JSON.
672 */
673
674 /**
675 * Get information about the currently playing playlist.
676 *
677 * @route GET /api/player/current
678 * @response 200 Object with a `playlist` member describing the current playlist.
679 */
680 39566 HttpResponsePtr Player::render_GET(const HttpRequestPtr& req) {
681
2/4
✓ Branch 0 taken 39566 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39566 times.
✗ Branch 3 not taken.
39566 auto pieces = getPathPieces(req->path());
682 39566 int plen = pieces.size();
683
684
1/2
✓ Branch 0 taken 39566 times.
✗ Branch 1 not taken.
39566 Json::Value result;
685
686
8/10
✓ Branch 0 taken 39561 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 39561 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 39561 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 25353 times.
✓ Branch 7 taken 14208 times.
✓ Branch 8 taken 25358 times.
✓ Branch 9 taken 14208 times.
39566 if ((plen == 1) || ((plen == 2) && (pieces[1] == "status"))) {
687
1/2
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
25358 result = GetStatusJSON();
688
4/8
✓ Branch 0 taken 25358 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25358 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 25358 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 25358 times.
✗ Branch 7 not taken.
101432 return makeStringResponse(SaveJsonToString(result, " "), 200, "application/json");
689
4/8
✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14208 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14208 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14208 times.
✗ Branch 7 not taken.
14208 } else if ((plen == 2) && (pieces[1] == "current")) {
690
1/2
✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
14208 Json::Value result;
691
1/2
✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
14208 Json::Value pl;
692
2/4
✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14208 times.
✗ Branch 3 not taken.
14208 pl = PlaylistSnapshot()->GetInfo();
693
2/4
✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14208 times.
✗ Branch 3 not taken.
14208 result["playlist"] = pl;
694
4/8
✓ Branch 0 taken 14208 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14208 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14208 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14208 times.
✗ Branch 7 not taken.
42624 return makeStringResponse(SaveJsonToString(result, " "), 200, "application/json");
695 14208 }
696
697 ✗ return makeStringResponse("Not Found", 404, "text/plain");
698 39566 }
699
700 ✗ HttpResponsePtr Player::render_POST(const HttpRequestPtr& req) {
701 ✗ auto pieces = getPathPieces(req->path());
702 ✗ int plen = pieces.size();
703
704 ✗ return makeStringResponse("Not Found", 404, "text/plain");
705 ✗ }
706
707 ✗ HttpResponsePtr Player::render_PUT(const HttpRequestPtr& req) {
708 ✗ auto pieces = getPathPieces(req->path());
709 ✗ int plen = pieces.size();
710
711 ✗ return makeStringResponse("Not Found", 404, "text/plain");
712 ✗ }
713