summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2026-01-31 00:59:50 +0100
committerCarl Hetherington <cth@carlh.net>2026-02-03 21:37:04 +0100
commitabfbd4d2b4b9a9a9cc8b566db5e4377030823592 (patch)
treee659dc6a3df21d65a92b77837aac32650064d433
parentf122cc879e5824cd93782dba1bdd207239f294ce (diff)
Support playlist load from the web interface.
-rw-r--r--src/lib/http_server.cc18
-rw-r--r--src/lib/http_server.h1
-rw-r--r--src/wx/player_frame.cc29
-rw-r--r--src/wx/player_frame.h8
-rw-r--r--src/wx/playlist_controls.cc4
-rw-r--r--web/index.html10
-rw-r--r--web/playlists.html4
7 files changed, 66 insertions, 8 deletions
diff --git a/src/lib/http_server.cc b/src/lib/http_server.cc
index 088a4c2bc..43b4cbb84 100644
--- a/src/lib/http_server.cc
+++ b/src/lib/http_server.cc
@@ -28,12 +28,14 @@
#include "show_playlist_list.h"
#include "util.h"
#include "variant.h"
+#include <dcp/raw_convert.h>
#include <nlohmann/json.hpp>
#include <boost/algorithm/string.hpp>
#include <stdexcept>
using std::make_pair;
+using std::pair;
using std::runtime_error;
using std::shared_ptr;
using std::string;
@@ -207,6 +209,22 @@ HTTPServer::post_request(string const& url, string const& body)
auto response = Response(303);
response.add_header("Location", "/");
return response;
+ } else if (url == "/api/v1/load-playlist") {
+ nlohmann::json details = nlohmann::json::parse(body);
+ vector<pair<string, boost::optional<float>>> entries;
+ for (auto const& entry: details["entries"]) {
+ if (!entry.contains("uuid")) {
+ continue;
+ }
+ boost::optional<float> crop;
+ if (entry.contains("crop_to_ratio")) {
+ crop = entry["crop_to_ratio"];
+ }
+ entries.push_back({entry["uuid"], crop});
+ }
+ emit(boost::bind(boost::ref(LoadPlaylist), entries));
+ /* XXX: return a failure if LoadPlaylist fails */
+ return Response(200);
} else if (url == "/api/v1/playlists") {
ShowPlaylist playlist("New Playlist");
ShowPlaylistList list;
diff --git a/src/lib/http_server.h b/src/lib/http_server.h
index 56f75047c..7ca5fa534 100644
--- a/src/lib/http_server.h
+++ b/src/lib/http_server.h
@@ -65,6 +65,7 @@ public:
boost::signals2::signal<void ()> Play;
boost::signals2::signal<void ()> Stop;
+ boost::signals2::signal<void (std::vector<std::pair<std::string, boost::optional<float>>>)> LoadPlaylist;
void set_playing(bool playing) {
boost::mutex::scoped_lock lm(_mutex);
diff --git a/src/wx/player_frame.cc b/src/wx/player_frame.cc
index afc43b807..5097c5e00 100644
--- a/src/wx/player_frame.cc
+++ b/src/wx/player_frame.cc
@@ -71,6 +71,7 @@ LIBDCP_ENABLE_WARNINGS
using std::dynamic_pointer_cast;
using std::exception;
using std::make_shared;
+using std::pair;
using std::shared_ptr;
using std::string;
using std::weak_ptr;
@@ -1168,6 +1169,7 @@ PlayerFrame::setup_http_server()
_http_server.reset(new HTTPServer(config->player_http_server_port()));
_http_server->Play.connect(boost::bind(&FilmViewer::start, &_viewer));
_http_server->Stop.connect(boost::bind(&FilmViewer::stop, &_viewer));
+ _http_server->LoadPlaylist.connect(boost::bind(&PlayerFrame::load_pair_playlist, this, _1));
_http_server_thread = boost::thread(boost::bind(&HTTPServer::run, _http_server.get()));
}
} catch (std::exception& e) {
@@ -1330,7 +1332,18 @@ get_kdm_from_directory(shared_ptr<DCPContent> dcp)
bool
-PlayerFrame::set_playlist(vector<ShowPlaylistEntry> playlist)
+PlayerFrame::load_playlist(vector<ShowPlaylistEntry> const& playlist)
+{
+ vector<pair<string, optional<float>>> details;
+ for (auto const& entry: playlist) {
+ details.push_back({entry.uuid(), entry.crop_to_ratio()});
+ }
+ return load_pair_playlist(details);
+}
+
+
+bool
+PlayerFrame::load_pair_playlist(vector<pair<string, optional<float>>> const& playlist)
{
bool was_playing = false;
if (_viewer.playing()) {
@@ -1346,7 +1359,7 @@ PlayerFrame::set_playlist(vector<ShowPlaylistEntry> playlist)
auto const store = ShowPlaylistContentStore::instance();
for (auto const& entry: playlist) {
dialog.Pulse();
- auto content = store->get(entry);
+ auto content = store->get(entry.first);
if (!content) {
error_dialog(this, _("This playlist cannot be loaded as some content is missing."));
_playlist.clear();
@@ -1374,7 +1387,7 @@ PlayerFrame::set_playlist(vector<ShowPlaylistEntry> playlist)
return false;
}
}
- _playlist.push_back({content, entry.crop_to_ratio()});
+ _playlist.push_back({content, entry.second});
}
take_playlist_entry();
@@ -1389,6 +1402,16 @@ PlayerFrame::set_playlist(vector<ShowPlaylistEntry> playlist)
}
+void
+PlayerFrame::clear_playlist()
+{
+ _playlist.clear();
+ _playlist_position = 0;
+ take_playlist_entry();
+ _controls->playlist_changed();
+}
+
+
/** Stop the viewer, take the thing at _playlist_position and prepare to play it.
* Set up to play nothing if the playlist is empty, or we're off the
* end of it.
diff --git a/src/wx/player_frame.h b/src/wx/player_frame.h
index e50bc7995..7663aa110 100644
--- a/src/wx/player_frame.h
+++ b/src/wx/player_frame.h
@@ -71,10 +71,14 @@ public:
void load_stress_script(boost::filesystem::path path);
void idle();
- /** Set the playlist. If we're currently playing, this will stop whatever is
+ /** Load some ShowPlaylistEntrys and set our playlist to them
+ * If we're currently playing, this will stop whatever is
* happening now and start playing this playlist.
+ * @return true on success, false on failure
*/
- bool set_playlist(std::vector<ShowPlaylistEntry> playlist);
+ bool load_playlist(std::vector<ShowPlaylistEntry> const& playlist);
+ bool load_pair_playlist(std::vector<std::pair<std::string, boost::optional<float>>> const& playlist);
+ void clear_playlist();
std::vector<std::shared_ptr<Content>> playlist() const;
diff --git a/src/wx/playlist_controls.cc b/src/wx/playlist_controls.cc
index a0f91b00f..8618f7921 100644
--- a/src/wx/playlist_controls.cc
+++ b/src/wx/playlist_controls.cc
@@ -202,7 +202,7 @@ PlaylistControls::stopped()
void
PlaylistControls::play_clicked()
{
- if (_player->set_playlist(_next_playlist)) {
+ if (_player->load_playlist(_next_playlist)) {
_viewer.start();
}
}
@@ -240,7 +240,7 @@ PlaylistControls::stop_clicked()
_paused = false;
_viewer.stop();
_viewer.seek(DCPTime(), true);
- _player->set_playlist({});
+ _player->clear_playlist();
}
diff --git a/web/index.html b/web/index.html
index b0b5b4505..91e3e0bf1 100644
--- a/web/index.html
+++ b/web/index.html
@@ -18,6 +18,13 @@ setInterval(function() {
});
}, 250);
function play() {
+ entries = [];
+ var nextPlaylist = document.getElementById('next-playlist').children[0].children;
+ for (var i = 0; i < nextPlaylist.length; i++) {
+ entries.push({"uuid": nextPlaylist[i].uuid, "crop_to_ratio": nextPlaylist[i].crop_to_ratio});
+ }
+ console.log(JSON.stringify({"entries": entries}));
+ fetch("/api/v1/load-playlist", { method: "POST", body: JSON.stringify({"entries": entries}) });
fetch("/api/v1/play", { method: "POST" });
}
function stop() {
@@ -49,9 +56,10 @@ function showPlaylists()
data.content.forEach(content => {
var li = document.createElement("li");
li.appendChild(document.createTextNode(content.name));
+ li.uuid = content.uuid;
+ li.crop_to_ratio = content.crop_to_ratio;
ul.appendChild(li);
});
- console.log(data);
})
});
};
diff --git a/web/playlists.html b/web/playlists.html
index 3d2e37f41..723b2ec6c 100644
--- a/web/playlists.html
+++ b/web/playlists.html
@@ -306,6 +306,8 @@ ul#content {
SIDEBAR
+<div style="margin-left: 15%;">
+
<div style="width: 45%; display: inline-block; margin-right: 3em;">
<p>Playlists: <button onclick="newPlaylist();">Add</button> <button id="remove-playlist" onclick="removeSelectedPlaylist();">Remove</button>
<ul id="playlists">
@@ -324,6 +326,8 @@ SIDEBAR
</ul>
</div>
+</div>
+
</body>
</html>