summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/config.cc12
-rw-r--r--src/lib/config.h20
-rw-r--r--src/lib/http_server.cc71
-rw-r--r--src/lib/show_playlist.cc (renamed from src/lib/spl_entry.h)44
-rw-r--r--src/lib/show_playlist.h75
-rw-r--r--src/lib/show_playlist_content_store.cc (renamed from src/lib/content_store.cc)23
-rw-r--r--src/lib/show_playlist_content_store.h (renamed from src/lib/content_store.h)15
-rw-r--r--src/lib/show_playlist_entry.cc103
-rw-r--r--src/lib/show_playlist_entry.h66
-rw-r--r--src/lib/show_playlist_list.cc223
-rw-r--r--src/lib/show_playlist_list.h83
-rw-r--r--src/lib/spl.cc107
-rw-r--r--src/lib/spl.h156
-rw-r--r--src/lib/spl_entry.cc75
-rw-r--r--src/lib/wscript7
-rw-r--r--src/tools/dcpomatic_player.cc3
-rw-r--r--src/tools/dcpomatic_playlist.cc293
-rw-r--r--src/wx/config_dialog.cc19
-rw-r--r--src/wx/config_dialog.h4
-rw-r--r--src/wx/content_view.cc41
-rw-r--r--src/wx/content_view.h12
-rw-r--r--src/wx/playlist_controls.cc120
-rw-r--r--src/wx/playlist_controls.h9
-rw-r--r--src/wx/wx_util.cc6
-rw-r--r--src/wx/wx_util.h2
25 files changed, 888 insertions, 701 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc
index 4440aab4d..10c4e4e01 100644
--- a/src/lib/config.cc
+++ b/src/lib/config.cc
@@ -192,7 +192,7 @@ Config::set_defaults()
_respect_kdm_validity_periods = true;
_player_debug_log_file = boost::none;
_player_content_directory = boost::none;
- _player_playlist_directory = boost::none;
+ _show_playlists_file = read_path("show_playlists.sqlite3");
_player_kdm_directory = boost::none;
_audio_mapping = boost::none;
_custom_languages.clear();
@@ -617,7 +617,7 @@ try
_respect_kdm_validity_periods = f.optional_bool_child("RespectKDMValidityPeriods").get_value_or(true);
_player_debug_log_file = f.optional_string_child("PlayerDebugLogFile");
_player_content_directory = f.optional_string_child("PlayerContentDirectory");
- _player_playlist_directory = f.optional_string_child("PlayerPlaylistDirectory");
+ _show_playlists_file = f.string_child("ShowPlaylistsFile");
_player_kdm_directory = f.optional_string_child("PlayerKDMDirectory");
if (f.optional_node_child("AudioMapping")) {
@@ -1093,10 +1093,8 @@ Config::write_config() const
/* [XML] PlayerContentDirectory Directory to use for player content in the dual-screen mode. */
cxml::add_text_child(root, "PlayerContentDirectory", _player_content_directory->string());
}
- if (_player_playlist_directory) {
- /* [XML] PlayerPlaylistDirectory Directory to use for player playlists in the dual-screen mode. */
- cxml::add_text_child(root, "PlayerPlaylistDirectory", _player_playlist_directory->string());
- }
+ /* [XML] ShowPlaylistsFile Filename of SQLite3 database containing show playlists for the player dual-screen mode */
+ cxml::add_text_child(root, "ShowPlaylistsFile", _show_playlists_file.string());
if (_player_kdm_directory) {
/* [XML] PlayerKDMDirectory Directory to use for player KDMs in the dual-screen mode. */
cxml::add_text_child(root, "PlayerKDMDirectory", _player_kdm_directory->string());
@@ -1697,7 +1695,7 @@ Config::load_from_zip(boost::filesystem::path zip_file, CinemasAction action)
changed(Property::SOUND);
changed(Property::SOUND_OUTPUT);
changed(Property::PLAYER_CONTENT_DIRECTORY);
- changed(Property::PLAYER_PLAYLIST_DIRECTORY);
+ changed(Property::SHOW_PLAYLISTS_FILE);
changed(Property::PLAYER_DEBUG_LOG);
changed(Property::HISTORY);
changed(Property::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS);
diff --git a/src/lib/config.h b/src/lib/config.h
index f598ffbc6..e0727fe57 100644
--- a/src/lib/config.h
+++ b/src/lib/config.h
@@ -108,7 +108,7 @@ public:
SOUND,
SOUND_OUTPUT,
PLAYER_CONTENT_DIRECTORY,
- PLAYER_PLAYLIST_DIRECTORY,
+ SHOW_PLAYLISTS_FILE,
PLAYER_DEBUG_LOG,
HISTORY,
SHOW_EXPERIMENTAL_AUDIO_PROCESSORS,
@@ -580,8 +580,8 @@ public:
return _player_content_directory;
}
- boost::optional<boost::filesystem::path> player_playlist_directory() const {
- return _player_playlist_directory;
+ boost::filesystem::path show_playlists_file() const {
+ return _show_playlists_file;
}
boost::optional<boost::filesystem::path> player_kdm_directory() const {
@@ -1134,16 +1134,8 @@ public:
changed(PLAYER_CONTENT_DIRECTORY);
}
- void set_player_playlist_directory(boost::filesystem::path p) {
- maybe_set(_player_playlist_directory, p, PLAYER_PLAYLIST_DIRECTORY);
- }
-
- void unset_player_playlist_directory() {
- if (!_player_playlist_directory) {
- return;
- }
- _player_playlist_directory = boost::none;
- changed(PLAYER_PLAYLIST_DIRECTORY);
+ void set_show_playlists_file(boost::filesystem::path p) {
+ maybe_set(_show_playlists_file, p, SHOW_PLAYLISTS_FILE);
}
void set_player_kdm_directory(boost::filesystem::path p) {
@@ -1462,7 +1454,7 @@ private:
for playback.
*/
boost::optional<boost::filesystem::path> _player_content_directory;
- boost::optional<boost::filesystem::path> _player_playlist_directory;
+ boost::filesystem::path _show_playlists_file;
boost::optional<boost::filesystem::path> _player_kdm_directory;
boost::optional<AudioMapping> _audio_mapping;
std::vector<dcp::LanguageTag> _custom_languages;
diff --git a/src/lib/http_server.cc b/src/lib/http_server.cc
index c69a8b496..495532704 100644
--- a/src/lib/http_server.cc
+++ b/src/lib/http_server.cc
@@ -20,12 +20,12 @@
#include "config.h"
-#include "content_store.h"
#include "cross.h"
#include "dcpomatic_log.h"
#include "dcpomatic_socket.h"
#include "http_server.h"
-#include "spl.h"
+#include "show_playlist_content_store.h"
+#include "show_playlist_list.h"
#include "util.h"
#include "variant.h"
#include <dcp/raw_convert.h>
@@ -98,27 +98,6 @@ Response::send(shared_ptr<Socket> socket)
}
-vector<shared_ptr<SignalSPL>>
-get_playlists()
-{
- vector<shared_ptr<SignalSPL>> playlists;
-
- if (auto path = Config::instance()->player_playlist_directory()) {
- try {
- for (auto i: dcp::filesystem::directory_iterator(*path)) {
- auto spl = make_shared<SignalSPL>();
- try {
- spl->read(i, ContentStore::instance());
- playlists.push_back(spl);
- } catch (...) {}
- }
- } catch (...) {}
- }
-
- return playlists;
-}
-
-
Response
HTTPServer::get(string const& url)
{
@@ -138,9 +117,10 @@ HTTPServer::get(string const& url)
response.set_type(Response::Type::JSON);
return response;
} else if (url == "/api/v1/playlists") {
+ ShowPlaylistList spl_list;
nlohmann::json json;
- for (auto spl: get_playlists()) {
- json.push_back(spl->as_json_without_content());
+ for (auto const& spl: spl_list.show_playlists()) {
+ json.push_back(spl.second.as_json());
}
auto response = Response(200, json.dump());
response.set_type(Response::Type::JSON);
@@ -151,18 +131,20 @@ HTTPServer::get(string const& url)
if (parts.size() != 5) {
return Response::ERROR_404;
}
- for (auto spl: get_playlists()) {
- if (spl->id() == parts[4]) {
- auto response = Response(200, spl->as_json_with_content().dump());
- response.set_type(Response::Type::JSON);
- return response;
+ ShowPlaylistList spl_list;
+ for (auto const& spl: spl_list.show_playlists()) {
+ if (spl.second.uuid() == parts[4]) {
+ // XXX
+ // auto response = Response(200, spl->as_json_with_content().dump());
+ // response.set_type(Response::Type::JSON);
+ // return response;
}
}
return Response::ERROR_404;
} else if (url == "/api/v1/content") {
nlohmann::json json;
- for (auto i: ContentStore::instance()->all()) {
- json.push_back(SPLEntry(i).as_json());
+ for (auto i: ShowPlaylistContentStore::instance()->all()) {
+ json.push_back(i.as_json());
}
auto response = Response(200, json.dump());
response.set_type(Response::Type::JSON);
@@ -173,11 +155,11 @@ HTTPServer::get(string const& url)
if (parts.size() != 5) {
return Response::ERROR_404;
}
- auto content = ContentStore::instance()->get(parts[4]);
+ auto content = ShowPlaylistContentStore::instance()->get(parts[4]);
if (!content) {
return Response::ERROR_404;
}
- auto json = SPLEntry(content).as_json();
+ auto json = content->as_json();
auto response = Response(200, json.dump());
response.set_type(Response::Type::JSON);
return response;
@@ -208,16 +190,18 @@ HTTPServer::post(string const& url, vector<uint8_t> const& body)
return Response::ERROR_404;
}
bool found = false;
- for (auto spl: get_playlists()) {
- if (spl->id() == parts[4]) {
+ ShowPlaylistList spl_list;
+ for (auto const& spl: spl_list.show_playlists()) {
+ if (spl.second.uuid() == parts[4]) {
nlohmann::json details = nlohmann::json::parse(body);
- spl->insert(
- SPLEntry(ContentStore::instance()->get(details["digest"])),
- details["before"].is_null() ? optional<string>() : details["before"].get<string>()
- );
- if (auto dir = Config::instance()->player_playlist_directory()) {
- spl->write(*dir / (spl->id() + ".xml"));
- }
+ // XXX
+ // spl->insert(
+ // SPLEntry(ContentStore::instance()->get(details["digest"])),
+ // details["before"].is_null() ? optional<string>() : details["before"].get<string>()
+ // );
+ // if (auto dir = Config::instance()->player_playlist_directory()) {
+ // spl->write(*dir / (spl->id() + ".xml"));
+ // }
found = true;
}
}
@@ -225,7 +209,6 @@ HTTPServer::post(string const& url, vector<uint8_t> const& body)
return Response::ERROR_404;
}
return Response(201);
-
} else {
return Response::ERROR_404;
}
diff --git a/src/lib/spl_entry.h b/src/lib/show_playlist.cc
index 11f2742bf..b10757d5a 100644
--- a/src/lib/spl_entry.h
+++ b/src/lib/show_playlist.cc
@@ -19,43 +19,15 @@
*/
-#ifndef DCPOMATIC_SPL_ENTRY_H
-#define DCPOMATIC_SPL_ENTRY_H
+#include "show_playlist.h"
-#include <libcxml/cxml.h>
-#include <dcp/content_kind.h>
-#include <nlohmann/json.hpp>
-
-
-namespace xmlpp {
- class Element;
-}
-
-class Content;
-
-
-/** An entry on a show playlist (SPL) */
-class SPLEntry
+nlohmann::json
+ShowPlaylist::as_json() const
{
-public:
- SPLEntry (std::shared_ptr<Content> c);
-
- void as_xml(xmlpp::Element* e) const;
- nlohmann::json as_json() const;
-
- std::shared_ptr<Content> content;
- std::string name;
- /** Digest of this content */
- std::string digest;
- /** CPL ID */
- std::string id;
- boost::optional<dcp::ContentKind> kind;
- bool encrypted;
-
-private:
- void construct (std::shared_ptr<Content> content);
-};
-
+ nlohmann::json json;
+ json["uuid"] = _uuid;
+ json["name"] = _name;
+ return json;
+}
-#endif
diff --git a/src/lib/show_playlist.h b/src/lib/show_playlist.h
new file mode 100644
index 000000000..e7f145d37
--- /dev/null
+++ b/src/lib/show_playlist.h
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2018-2020 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef DCPOMATIC_SHOW_PLAYLIST_H
+#define DCPOMATIC_SHOW_PLAYLIST_H
+
+
+#include <dcp/util.h>
+#include <nlohmann/json.hpp>
+
+
+/** @class ShowPlaylist
+ *
+ * @brief A "show playlist": what a projection system might play for an entire cinema "show".
+ *
+ * For example, it might contain some adverts, some trailers and a feature.
+ * Each SPL has unique ID, a name, and some ordered entries (the content). The content
+ * is not stored in this class, but can be read from the database via SPLList.
+ */
+class ShowPlaylist
+{
+public:
+ ShowPlaylist()
+ : _uuid(dcp::make_uuid())
+ {}
+
+ explicit ShowPlaylist(std::string name)
+ : _uuid(dcp::make_uuid())
+ , _name(name)
+ {}
+
+ ShowPlaylist(std::string uuid, std::string name)
+ : _uuid(uuid)
+ , _name(name)
+ {}
+
+ std::string uuid() const {
+ return _uuid;
+ }
+
+ std::string name() const {
+ return _name;
+ }
+
+ void set_name(std::string name) {
+ _name = name;
+ }
+
+ nlohmann::json as_json() const;
+
+private:
+ std::string _uuid;
+ std::string _name;
+};
+
+
+#endif
diff --git a/src/lib/content_store.cc b/src/lib/show_playlist_content_store.cc
index 10f3fff04..41d9db245 100644
--- a/src/lib/content_store.cc
+++ b/src/lib/show_playlist_content_store.cc
@@ -21,11 +21,11 @@
#include "config.h"
#include "content_factory.h"
-#include "content_store.h"
#include "cross.h"
#include "dcp_content.h"
#include "examine_content_job.h"
#include "job_manager.h"
+#include "show_playlist_content_store.h"
#include "util.h"
#include <dcp/exceptions.h>
#include <dcp/filesystem.h>
@@ -36,13 +36,14 @@ using std::pair;
using std::shared_ptr;
using std::string;
using std::vector;
+using boost::optional;
-ContentStore* ContentStore::_instance = nullptr;
+ShowPlaylistContentStore* ShowPlaylistContentStore::_instance = nullptr;
vector<pair<string, string>>
-ContentStore::update(std::function<bool()> pulse)
+ShowPlaylistContentStore::update(std::function<bool()> pulse)
{
_content.clear();
auto dir = Config::instance()->player_content_directory();
@@ -97,7 +98,7 @@ ContentStore::update(std::function<bool()> pulse)
if (i->finished_in_error()) {
errors.push_back({String::compose("%1.\n", i->error_summary()), i->error_details()});
} else {
- _content.push_back(i->content());
+ _content.push_back(ShowPlaylistEntry(i->content()));
}
}
@@ -105,11 +106,11 @@ ContentStore::update(std::function<bool()> pulse)
}
-shared_ptr<Content>
-ContentStore::get(string digest) const
+optional<ShowPlaylistEntry>
+ShowPlaylistContentStore::get(string uuid) const
{
- auto iter = std::find_if(_content.begin(), _content.end(), [digest](shared_ptr<const Content> content) {
- return content->digest() == digest;
+ auto iter = std::find_if(_content.begin(), _content.end(), [uuid](ShowPlaylistEntry const& entry) {
+ return entry.uuid() == uuid;
});
if (iter == _content.end()) {
@@ -120,11 +121,11 @@ ContentStore::get(string digest) const
}
-ContentStore*
-ContentStore::instance()
+ShowPlaylistContentStore*
+ShowPlaylistContentStore::instance()
{
if (!_instance) {
- _instance = new ContentStore();
+ _instance = new ShowPlaylistContentStore();
}
return _instance;
diff --git a/src/lib/content_store.h b/src/lib/show_playlist_content_store.h
index dcf31b5a4..dcf19cebe 100644
--- a/src/lib/content_store.h
+++ b/src/lib/show_playlist_content_store.h
@@ -19,6 +19,7 @@
*/
+#include "show_playlist_entry.h"
#include <functional>
#include <memory>
#include <vector>
@@ -26,13 +27,13 @@
class Content;
-/** @class ContentStore
+/** @class ShowPlaylistContentStore
* @brief Class to maintain details of what content we have available to play
*/
-class ContentStore
+class ShowPlaylistContentStore
{
public:
- std::shared_ptr<Content> get(std::string digest) const;
+ boost::optional<ShowPlaylistEntry> get(std::string uuid) const;
/** Examine content in the configured directory and update our list.
* @param pulse Called every so often to indicate progress. Return false to cancel the scan.
@@ -40,14 +41,14 @@ public:
*/
std::vector<std::pair<std::string, std::string>> update(std::function<bool()> pulse);
- static ContentStore* instance();
+ static ShowPlaylistContentStore* instance();
- std::vector<std::shared_ptr<Content>> const& all() const {
+ std::vector<ShowPlaylistEntry> const& all() const {
return _content;
}
private:
- std::vector<std::shared_ptr<Content>> _content;
+ std::vector<ShowPlaylistEntry> _content;
- static ContentStore* _instance;
+ static ShowPlaylistContentStore* _instance;
};
diff --git a/src/lib/show_playlist_entry.cc b/src/lib/show_playlist_entry.cc
new file mode 100644
index 000000000..a684de1d2
--- /dev/null
+++ b/src/lib/show_playlist_entry.cc
@@ -0,0 +1,103 @@
+/*
+ Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "dcp_content.h"
+#include "dcpomatic_assert.h"
+#include "show_playlist_entry.h"
+#include <fmt/format.h>
+
+
+using std::dynamic_pointer_cast;
+using std::shared_ptr;
+using std::string;
+
+
+ShowPlaylistEntry::ShowPlaylistEntry(shared_ptr<Content> content)
+ : _content(content)
+{
+
+}
+
+
+string
+ShowPlaylistEntry::uuid() const
+{
+ if (auto dcp = dynamic_pointer_cast<DCPContent>(_content)) {
+ DCPOMATIC_ASSERT(dcp->cpl());
+ return *dcp->cpl();
+ } else {
+ return _content->digest();
+ }
+}
+
+
+string
+ShowPlaylistEntry::name() const
+{
+ if (auto dcp = dynamic_pointer_cast<DCPContent>(_content)) {
+ return dcp->name();
+ } else {
+ return _content->path(0).filename().string();
+ }
+}
+
+
+dcp::ContentKind
+ShowPlaylistEntry::kind() const
+{
+ if (auto dcp = dynamic_pointer_cast<DCPContent>(_content)) {
+ return dcp->content_kind().get_value_or(dcp::ContentKind::FEATURE);
+ } else {
+ return dcp::ContentKind::FEATURE;
+ }
+}
+
+
+bool
+ShowPlaylistEntry::encrypted() const
+{
+ if (auto dcp = dynamic_pointer_cast<DCPContent>(_content)) {
+ return dcp->encrypted();
+ } else {
+ return false;
+ }
+}
+
+
+string
+ShowPlaylistEntry::approximate_length() const
+{
+ auto const hmsf = _content->approximate_length().split(24);
+ return fmt::format("{:02d}:{:02d}:{:02d}", hmsf.h, hmsf.m, hmsf.s);
+}
+
+
+nlohmann::json
+ShowPlaylistEntry::as_json() const
+{
+ nlohmann::json json;
+ json["uuid"] = uuid();
+ json["name"] = name();
+ json["kind"] = kind().name();
+ json["encrypted"] = encrypted();
+ return json;
+}
+
diff --git a/src/lib/show_playlist_entry.h b/src/lib/show_playlist_entry.h
new file mode 100644
index 000000000..9a20ca3b7
--- /dev/null
+++ b/src/lib/show_playlist_entry.h
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef DCPOMATIC_SHOW_PLAYLIST_ENTRY_H
+#define DCPOMATIC_SHOW_PLAYLIST_ENTRY_H
+
+
+#include <dcp/content_kind.h>
+#include <nlohmann/json.hpp>
+
+
+class Content;
+
+
+/** @class ShowPlaylistEntry
+ *
+ * @brief An entry on a show playlist (SPL).
+ *
+ * In the ShowPlaylist database we only store the UUID of the content. This class
+ * holds a pointer to a Content object, and provides a simpler interface to information
+ * that is needed by the ShowPlaylist code.
+ *
+ * Given a UUID from the database, a ShowPlaylistEntry can be obtained from the
+ * ShowPlaylistContentStore.
+ */
+class ShowPlaylistEntry
+{
+public:
+ explicit ShowPlaylistEntry(std::shared_ptr<Content> content);
+
+ nlohmann::json as_json() const;
+
+ std::string uuid() const;
+ std::string name() const;
+ dcp::ContentKind kind() const;
+ std::string approximate_length() const;
+ bool encrypted() const;
+
+ std::shared_ptr<Content> content() const {
+ return _content;
+ }
+
+private:
+ std::shared_ptr<Content> _content;
+};
+
+
+#endif
diff --git a/src/lib/show_playlist_list.cc b/src/lib/show_playlist_list.cc
new file mode 100644
index 000000000..a45d42d12
--- /dev/null
+++ b/src/lib/show_playlist_list.cc
@@ -0,0 +1,223 @@
+/*
+ Copyright (C) 2025 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "config.h"
+#include "show_playlist.h"
+#include "show_playlist.h"
+#include "show_playlist_entry.h"
+#include "show_playlist_list.h"
+#include "sqlite_statement.h"
+#include "sqlite_transaction.h"
+
+
+using std::make_pair;
+using std::pair;
+using std::string;
+using std::vector;
+using boost::optional;
+
+
+ShowPlaylistList::ShowPlaylistList()
+ : _show_playlists("show_playlists")
+ , _entries("entries")
+ , _db(Config::instance()->show_playlists_file())
+{
+ setup_tables();
+ setup();
+}
+
+
+ShowPlaylistList::ShowPlaylistList(boost::filesystem::path db_file)
+ : _show_playlists("show_playlists")
+ , _entries("entries")
+ , _db(db_file)
+{
+ setup_tables();
+ setup();
+}
+
+
+void
+ShowPlaylistList::setup_tables()
+{
+ _show_playlists.add_column("uuid", "TEXT");
+ _show_playlists.add_column("name", "TEXT");
+
+ _entries.add_column("show_playlist", "INTEGER");
+ _entries.add_column("uuid", "TEXT");
+ _entries.add_column("sort_index", "INTEGER");
+}
+
+
+void
+ShowPlaylistList::setup()
+{
+ SQLiteStatement show_playlists(_db, _show_playlists.create());
+ show_playlists.execute();
+
+ SQLiteStatement entries(_db, _entries.create());
+ entries.execute();
+}
+
+
+ShowPlaylistID
+ShowPlaylistList::add_show_playlist(ShowPlaylist const& playlist)
+{
+ SQLiteStatement statement(_db, _show_playlists.insert());
+
+ statement.bind_text(1, playlist.uuid());
+ statement.bind_text(2, playlist.name());
+
+ statement.execute();
+
+ return sqlite3_last_insert_rowid(_db.db());
+}
+
+
+void
+ShowPlaylistList::remove_show_playlist(ShowPlaylistID id)
+{
+ SQLiteStatement statement(_db, "DELETE FROM show_playlists WHERE ID=?");
+ statement.bind_int64(1, id.get());
+ statement.execute();
+}
+
+
+static
+vector<pair<ShowPlaylistID, ShowPlaylist>>
+show_playlists_from_result(SQLiteStatement& statement)
+{
+ vector<pair<ShowPlaylistID, ShowPlaylist>> output;
+
+ statement.execute([&output](SQLiteStatement& statement) {
+ DCPOMATIC_ASSERT(statement.data_count() == 3);
+ ShowPlaylistID const id = statement.column_int64(0);
+ auto const uuid = statement.column_text(1);
+ auto const name = statement.column_text(2);
+ output.push_back(make_pair(id, ShowPlaylist(uuid, name)));
+ });
+
+ return output;
+}
+
+
+vector<pair<ShowPlaylistID, ShowPlaylist>>
+ShowPlaylistList::show_playlists() const
+{
+ SQLiteStatement statement(_db, _show_playlists.select("ORDER BY name COLLATE unicode ASC"));
+ return show_playlists_from_result(statement);
+}
+
+
+
+vector<string>
+ShowPlaylistList::entries(ShowPlaylistID show_playlist_id) const
+{
+ SQLiteStatement statement(_db, "SELECT entries.uuid FROM entries JOIN show_playlists ON entries.show_playlist=show_playlists.id WHERE show_playlists.id=? ORDER BY entries.sort_index");
+ statement.bind_int64(1, show_playlist_id.get());
+
+ vector<string> output;
+
+ statement.execute([&output](SQLiteStatement& statement) {
+ DCPOMATIC_ASSERT(statement.data_count() == 1);
+ output.push_back(statement.column_text(0));
+ });
+
+ return output;
+}
+
+
+vector<string>
+ShowPlaylistList::entries(string const& show_playlist_uuid) const
+{
+ SQLiteStatement statement(_db, "SELECT entries.uuid FROM entries JOIN show_playlists ON entries.show_playlist=show_playlists.id WHERE show_playlists.uuid=? ORDER BY entries.sort_index");
+ statement.bind_text(1, show_playlist_uuid);
+
+ vector<string> output;
+
+ statement.execute([&output](SQLiteStatement& statement) {
+ DCPOMATIC_ASSERT(statement.data_count() == 1);
+ output.push_back(statement.column_text(0));
+ });
+
+ return output;
+}
+
+
+void
+ShowPlaylistList::add_entry(ShowPlaylistID playlist_id, ShowPlaylistEntry entry)
+{
+ SQLiteTransaction transaction(_db);
+
+ SQLiteStatement find_last_screen(_db, "SELECT MAX(sort_index) FROM entries WHERE show_playlist=?");
+ find_last_screen.bind_int64(1, playlist_id.get());
+
+ optional<int> highest_index;
+ find_last_screen.execute([&highest_index](SQLiteStatement& statement) {
+ if (statement.data_count() == 1) {
+ highest_index = statement.column_int64(0);
+ }
+ });
+
+ SQLiteStatement add_entry(_db, _entries.insert());
+
+ add_entry.bind_int64(1, playlist_id.get());
+ add_entry.bind_text(2, entry.uuid());
+ add_entry.bind_int64(3, highest_index ? *highest_index + 1 : 0);
+
+ add_entry.execute();
+
+ transaction.commit();
+}
+
+
+void
+ShowPlaylistList::move_entry_up(ShowPlaylistID playlist_id, int index)
+{
+ DCPOMATIC_ASSERT(index >= 1);
+
+ SQLiteTransaction transaction(_db);
+
+ SQLiteStatement find(_db, "SELECT id,sort_index FROM entries WHERE show_playlist=? ORDER BY sort_index LIMIT 2 OFFSET ?");
+ find.bind_int64(1, playlist_id.get());
+ find.bind_int64(2, index - 1);
+
+ vector<pair<int64_t, int64_t>> rows;
+ find.execute([&rows](SQLiteStatement& statement) {
+ DCPOMATIC_ASSERT(statement.data_count() == 2);
+ rows.push_back({statement.column_int64(0), statement.column_int64(1)});
+ });
+
+ DCPOMATIC_ASSERT(rows.size() == 2);
+
+ SQLiteStatement swap1(_db, "UPDATE entries SET sort_index=? WHERE id=?");
+ swap1.bind_int64(1, rows[0].second);
+ swap1.bind_int64(2, rows[1].first);
+ swap1.execute();
+
+ SQLiteStatement swap2(_db, "UPDATE entries SET sort_index=? WHERE id=?");
+ swap2.bind_int64(1, rows[1].second);
+ swap2.bind_int64(2, rows[0].first);
+ swap2.execute();
+
+ transaction.commit();
+}
+
diff --git a/src/lib/show_playlist_list.h b/src/lib/show_playlist_list.h
new file mode 100644
index 000000000..08414661d
--- /dev/null
+++ b/src/lib/show_playlist_list.h
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2025 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef DCPOMATIC_SHOW_PLAYLIST_LIST_H
+#define DCPOMATIC_SHOW_PLAYLIST_LIST_H
+
+
+#include "id.h"
+#include "show_playlist.h"
+#include "sqlite_database.h"
+#include "sqlite_table.h"
+
+
+/** @class ShowPlaylistID
+ *
+ * @brief The SQLite ID (not the UUID) of a ShowPlaylist.
+ */
+class ShowPlaylistID : public ID
+{
+public:
+ ShowPlaylistID(sqlite3_int64 id)
+ : ID(id) {}
+
+ bool operator<(ShowPlaylistID const& other) const {
+ return get() < other.get();
+ }
+};
+
+
+/** @class ShowPlaylistList
+ *
+ * @brief A list of SPLs (show playlists) stored in a SQLite database.
+ *
+ * A SPL (show playlist) is a list of content (and maybe later automation cues)
+ * that make up a "show" in a cinema/theater. For example, a SPL might contain
+ * some adverts, some trailers, and a feature.
+ */
+class ShowPlaylistList
+{
+public:
+ ShowPlaylistList();
+ explicit ShowPlaylistList(boost::filesystem::path db_file);
+
+ ShowPlaylistID add_show_playlist(ShowPlaylist const& show_playlist);
+ void remove_show_playlist(ShowPlaylistID id);
+ std::vector<std::pair<ShowPlaylistID, ShowPlaylist>> show_playlists() const;
+
+ std::vector<std::string> entries(ShowPlaylistID show_playlist_id) const;
+ std::vector<std::string> entries(std::string const& show_playlist_uuid) const;
+
+ void add_entry(ShowPlaylistID, ShowPlaylistEntry entry);
+ void move_entry_up(ShowPlaylistID, int index);
+
+private:
+ void setup_tables();
+ void setup();
+
+ SQLiteTable _show_playlists;
+ SQLiteTable _entries;
+ mutable SQLiteDatabase _db;
+};
+
+
+#endif
+
diff --git a/src/lib/spl.cc b/src/lib/spl.cc
deleted file mode 100644
index 89cb1d0c5..000000000
--- a/src/lib/spl.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#include "content_store.h"
-#include "spl.h"
-#include <libcxml/cxml.h>
-#include <dcp/filesystem.h>
-#include <dcp/warnings.h>
-LIBDCP_DISABLE_WARNINGS
-#include <libxml++/libxml++.h>
-LIBDCP_ENABLE_WARNINGS
-#include <iostream>
-
-
-using std::cout;
-using std::string;
-using std::shared_ptr;
-using boost::optional;
-
-
-void
-SPL::read (boost::filesystem::path path, ContentStore* store)
-{
- _spl.clear ();
- _missing = false;
- cxml::Document doc ("SPL");
- doc.read_file(dcp::filesystem::fix_long_path(path));
- _id = doc.string_child("Id");
- _name = doc.string_child("Name");
- for (auto i: doc.node_children("Entry")) {
- auto c = store->get(i->string_child("Digest"));
- if (c) {
- add (SPLEntry(c));
- } else {
- _missing = true;
- }
- }
-}
-
-
-void
-SPL::write (boost::filesystem::path path) const
-{
- xmlpp::Document doc;
- auto root = doc.create_root_node ("SPL");
- cxml::add_text_child(root, "Id", _id);
- cxml::add_text_child(root, "Name", _name);
- for (auto i: _spl) {
- i.as_xml(cxml::add_child(root, "Entry"));
- }
- doc.write_to_file_formatted (path.string());
-}
-
-
-void
-SPL::insert(SPLEntry entry, optional<string> before_id)
-{
- if (before_id) {
- auto iter = std::find_if(_spl.begin(), _spl.end(), [before_id](SPLEntry const& e) {
- return e.id == before_id;
- });
- if (iter != _spl.end()) {
- _spl.insert(iter, entry);
- }
- } else {
- _spl.push_back(entry);
- }
-}
-
-
-nlohmann::json
-SPL::as_json_without_content() const
-{
- nlohmann::json json;
- json["id"] = _id;
- json["name"] = _name;
- return json;
-}
-
-
-nlohmann::json
-SPL::as_json_with_content() const
-{
- auto json = as_json_without_content();
- for (auto i: _spl) {
- json["content"].push_back(i.as_json());
- }
- return json;
-}
diff --git a/src/lib/spl.h b/src/lib/spl.h
deleted file mode 100644
index ca81ff5af..000000000
--- a/src/lib/spl.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- Copyright (C) 2018-2020 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#ifndef DCPOMATIC_SPL_H
-#define DCPOMATIC_SPL_H
-
-
-#include "spl_entry.h"
-#include <dcp/util.h>
-#include <nlohmann/json.hpp>
-#include <boost/signals2.hpp>
-#include <algorithm>
-
-
-class ContentStore;
-
-
-/** @class SPL
- *
- * @brief A "show playlist": what a projection system might play for an entire cinema "show".
- *
- * For example, it might contain some adverts, some trailers and a feature.
- * Each SPL has unique ID, a name, and some SPLEntry objects.
- */
-class SPL
-{
-public:
- SPL()
- : _id(dcp::make_uuid())
- {}
-
- explicit SPL(std::string name)
- : _id(dcp::make_uuid())
- , _name(name)
- {}
-
-
- void add(SPLEntry e) {
- _spl.push_back(e);
- }
-
- void remove(std::size_t index) {
- _spl.erase(_spl.begin() + index);
- }
-
- void insert(SPLEntry entry, boost::optional<std::string> before_id);
-
- std::vector<SPLEntry> const& get() const {
- return _spl;
- }
-
- SPLEntry const& operator[](std::size_t index) const {
- return _spl[index];
- }
-
- void swap(size_t a, size_t b) {
- std::iter_swap(_spl.begin() + a, _spl.begin() + b);
- }
-
- void read(boost::filesystem::path path, ContentStore* store);
- void write(boost::filesystem::path path) const;
-
- std::string id() const {
- return _id;
- }
-
- std::string name() const {
- return _name;
- }
-
- void set_name(std::string name) {
- _name = name;
- }
-
- /** @return true if any content was missing when read() was last called on this SPL */
- bool missing() const {
- return _missing;
- }
-
- nlohmann::json as_json_without_content() const;
- nlohmann::json as_json_with_content() const;
-
-private:
- std::string _id;
- std::string _name;
- std::vector<SPLEntry> _spl;
- /** true if any content was missing when read() was last called on this SPL */
- bool _missing = false;
-};
-
-
-/** @class SignalSPL
- *
- * @brief A wrapper around SPL that signals changes.
- */
-class SignalSPL : public SPL
-{
-public:
- enum class Change {
- NAME,
- CONTENT,
- };
-
- SignalSPL() = default;
-
- explicit SignalSPL(std::string name)
- : SPL(name)
- {}
-
- void set_name(std::string name) {
- SPL::set_name (name);
- Changed(Change::NAME);
- }
-
- void add(SPLEntry e) {
- SPL::add(e);
- Changed(Change::CONTENT);
- }
-
- void remove(std::size_t index) {
- SPL::remove(index);
- Changed(Change::CONTENT);
- }
-
- void insert(SPLEntry e, boost::optional<std::string> before_id) {
- SPL::insert(e, before_id);
- Changed(Change::CONTENT);
- }
-
- void swap(size_t a, size_t b) {
- SPL::swap(a, b);
- Changed(Change::CONTENT);
- }
-
- boost::signals2::signal<void (Change)> Changed;
-};
-
-#endif
diff --git a/src/lib/spl_entry.cc b/src/lib/spl_entry.cc
deleted file mode 100644
index 476744252..000000000
--- a/src/lib/spl_entry.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- DCP-o-matic is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-
-#include "dcp_content.h"
-#include "dcpomatic_assert.h"
-#include "spl_entry.h"
-#include <dcp/warnings.h>
-LIBDCP_DISABLE_WARNINGS
-#include <libxml++/libxml++.h>
-LIBDCP_ENABLE_WARNINGS
-
-
-using std::shared_ptr;
-using std::dynamic_pointer_cast;
-
-
-SPLEntry::SPLEntry (shared_ptr<Content> c)
- : content (c)
- , digest (content->digest())
-{
- auto dcp = dynamic_pointer_cast<DCPContent> (content);
- if (dcp) {
- name = dcp->name ();
- DCPOMATIC_ASSERT (dcp->cpl());
- id = *dcp->cpl();
- kind = dcp->content_kind().get_value_or(dcp::ContentKind::FEATURE);
- encrypted = dcp->encrypted ();
- } else {
- name = content->path(0).filename().string();
- kind = dcp::ContentKind::FEATURE;
- }
-}
-
-
-void
-SPLEntry::as_xml(xmlpp::Element* e) const
-{
- cxml::add_text_child(e, "Digest", digest);
-}
-
-
-nlohmann::json
-SPLEntry::as_json() const
-{
- nlohmann::json json;
- json["name"] = name;
- json["digest"] = digest;
- if (!id.empty()) {
- json["cpl-id"] = id;
- }
- if (kind) {
- json["kind"] = kind->name();
- }
- json["encrypted"] = encrypted;
- return json;
-}
-
diff --git a/src/lib/wscript b/src/lib/wscript
index f9ee45ed5..250771108 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -57,7 +57,6 @@ sources = """
config.cc
content.cc
content_factory.cc
- content_store.cc
combine_dcp_job.cc
copy_dcp_details_to_film.cc
cpu_j2k_encoder_thread.cc
@@ -189,10 +188,12 @@ sources = """
send_notification_email_job.cc
send_problem_report_job.cc
server.cc
+ show_playlist.cc
+ show_playlist_content_store.cc
+ show_playlist_entry.cc
+ show_playlist_list.cc
shuffler.cc
state.cc
- spl.cc
- spl_entry.cc
sqlite_database.cc
sqlite_statement.cc
sqlite_table.cc
diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc
index 071329ae4..c7b4de9e0 100644
--- a/src/tools/dcpomatic_player.cc
+++ b/src/tools/dcpomatic_player.cc
@@ -253,7 +253,8 @@ public:
Bind(wxEVT_CLOSE_WINDOW, boost::bind(&DOMFrame::close, this, _1));
if (Config::instance()->player_mode() == Config::PLAYER_MODE_DUAL || Config::instance()->enable_player_http_server()) {
- update_content_store();
+ std::cout << "OK FUCKS\n";
+ update_show_playlist_content_store();
}
if (Config::instance()->player_mode() == Config::PLAYER_MODE_DUAL) {
diff --git a/src/tools/dcpomatic_playlist.cc b/src/tools/dcpomatic_playlist.cc
index 4f6ea59e7..40084f899 100644
--- a/src/tools/dcpomatic_playlist.cc
+++ b/src/tools/dcpomatic_playlist.cc
@@ -31,8 +31,9 @@
#include "lib/cross.h"
#include "lib/dcp_content.h"
#include "lib/film.h"
-#include "lib/spl.h"
-#include "lib/spl_entry.h"
+#include "lib/show_playlist.h"
+#include "lib/show_playlist_entry.h"
+#include "lib/show_playlist_list.h"
#include <dcp/filesystem.h>
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
@@ -49,6 +50,7 @@ using std::exception;
using std::make_pair;
using std::make_shared;
using std::map;
+using std::pair;
using std::shared_ptr;
using std::string;
using std::vector;
@@ -61,14 +63,15 @@ using namespace boost::placeholders;
#endif
-static
-void
-save_playlist(shared_ptr<const SPL> playlist)
-{
- if (auto dir = Config::instance()->player_playlist_directory()) {
- playlist->write(*dir / (playlist->id() + ".xml"));
- }
-}
+// XXX
+// static
+// void
+// save_playlist(shared_ptr<const SPL> playlist)
+// {
+// if (auto dir = Config::instance()->player_playlist_directory()) {
+// playlist->write(*dir / (playlist->id() + ".xml"));
+// }
+// }
class ContentDialog : public wxDialog
@@ -96,9 +99,9 @@ public:
_config_changed_connection = Config::instance()->Changed.connect(boost::bind(&ContentView::update, _content_view));
}
- shared_ptr<Content> selected () const
+ optional<ShowPlaylistEntry> selected() const
{
- return _content_view->selected ();
+ return _content_view->selected();
}
private:
@@ -138,7 +141,7 @@ public:
_sizer->Add (list);
- load_playlists ();
+ add_playlists_to_model_and_view();
_list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, bind(&PlaylistList::selection_changed, this));
_list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, bind(&PlaylistList::selection_changed, this));
@@ -153,16 +156,16 @@ public:
return _sizer;
}
- shared_ptr<SignalSPL> first_playlist () const
+ ShowPlaylist first_playlist() const
{
if (_playlists.empty()) {
return {};
}
- return _playlists.front ();
+ return _playlists.front().second;
}
- boost::signals2::signal<void (shared_ptr<SignalSPL>)> Edit;
+ boost::signals2::signal<void (boost::optional<std::pair<ShowPlaylistID, ShowPlaylist>>)> Edit;
private:
void setup_sensitivity()
@@ -170,20 +173,21 @@ private:
_delete->Enable(static_cast<bool>(selected()));
}
- void add_playlist_to_view (shared_ptr<const SignalSPL> playlist)
+ void add_playlist_to_view(ShowPlaylist playlist)
{
wxListItem item;
- item.SetId (_list->GetItemCount());
- long const N = _list->InsertItem (item);
- _list->SetItem (N, 0, std_to_wx(playlist->name()));
+ item.SetId(_list->GetItemCount());
+ long const N = _list->InsertItem(item);
+ _list->SetItem(N, 0, std_to_wx(playlist.name()));
}
- void add_playlist_to_model (shared_ptr<SignalSPL> playlist)
+ void add_playlist_to_model(ShowPlaylistID id, ShowPlaylist playlist)
{
- _playlists.push_back (playlist);
- playlist->Changed.connect(bind(&PlaylistList::changed, this, weak_ptr<SignalSPL>(playlist), _1));
+ _playlists.push_back(make_pair(id, playlist));
}
+#if 0
+ XXX
void changed(weak_ptr<SignalSPL> wp, SignalSPL::Change change)
{
auto playlist = wp.lock ();
@@ -208,43 +212,33 @@ private:
break;
}
}
+#endif
- void load_playlists ()
+ void add_playlists_to_model_and_view()
{
- auto path = Config::instance()->player_playlist_directory();
- if (!path) {
- return;
- }
+ ShowPlaylistList spl_list;
- _list->DeleteAllItems ();
- _playlists.clear ();
- try {
- for (auto i: dcp::filesystem::directory_iterator(*path)) {
- auto spl = make_shared<SignalSPL>();
- try {
- spl->read(i, ContentStore::instance());
- add_playlist_to_model (spl);
- } catch (...) {}
- }
- } catch (...) {}
+ _list->DeleteAllItems();
+ _playlists.clear();
- for (auto i: _playlists) {
- add_playlist_to_view (i);
+ for (auto const& spl: spl_list.show_playlists()) {
+ add_playlist_to_model(spl.first, spl.second);
+ }
+
+ for (auto const& i: _playlists) {
+ add_playlist_to_view(i.second);
}
}
void new_playlist ()
{
- auto dir = Config::instance()->player_playlist_directory();
- if (!dir) {
- error_dialog(_parent, _("No playlist folder is specified in preferences. Please set one and then try again."));
- return;
- }
+ ShowPlaylistList spl_list;
+ auto playlist = ShowPlaylist(wx_to_std(_("New Playlist")));
+ auto id = spl_list.add_show_playlist(playlist);
- shared_ptr<SignalSPL> spl (new SignalSPL(wx_to_std(_("New Playlist"))));
- add_playlist_to_model (spl);
- add_playlist_to_view (spl);
- _list->SetItemState (_list->GetItemCount() - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+ add_playlist_to_model(id, playlist);
+ add_playlist_to_view(playlist);
+ _list->SetItemState(_list->GetItemCount() - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
}
boost::optional<int> selected() const
@@ -264,25 +258,23 @@ private:
return;
}
- auto dir = Config::instance()->player_playlist_directory();
- if (!dir) {
- return;
- }
+ // ShowPlaylistList spl_list;
+ // spl_list.remove_show_playlist(_playlists[*index].first);
- dcp::filesystem::remove(*dir / (_playlists[*index]->id() + ".xml"));
- _list->DeleteItem(*index);
- _playlists.erase(_playlists.begin() + *index);
+ // _list->DeleteItem(*index);
+ // _playlists.erase(_playlists.begin() + *index);
- Edit(shared_ptr<SignalSPL>());
+ // XXX
+ // Edit(shared_ptr<SignalSPL>());
}
void selection_changed ()
{
long int selected = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (selected < 0 || selected >= int(_playlists.size())) {
- Edit (shared_ptr<SignalSPL>());
+ Edit({});
} else {
- Edit (_playlists[selected]);
+ Edit(_playlists[selected]);
}
setup_sensitivity();
@@ -292,38 +284,44 @@ private:
wxListCtrl* _list;
wxButton* _new;
wxButton* _delete;
- vector<shared_ptr<SignalSPL>> _playlists;
+ vector<pair<ShowPlaylistID, ShowPlaylist>> _playlists;
wxWindow* _parent;
};
+/** @class PlaylistContent
+ *
+ * @brief List showing the contents of a playlist
+ *
+ * This includes a heading, and buttons to re-order, add and remove items.
+ */
class PlaylistContent
{
public:
- PlaylistContent (wxPanel* parent, ContentDialog* content_dialog)
- : _content_dialog (content_dialog)
- , _sizer (new wxBoxSizer(wxVERTICAL))
- {
- auto title = new wxBoxSizer (wxHORIZONTAL);
- auto label = new wxStaticText (parent, wxID_ANY, wxEmptyString);
- label->SetLabelMarkup (_("<b>Playlist:</b>"));
- title->Add (label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, DCPOMATIC_SIZER_GAP);
- _name = new wxTextCtrl (parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400, -1));
- title->Add (_name, 0, wxRIGHT, DCPOMATIC_SIZER_GAP);
+ PlaylistContent(wxPanel* parent, ContentDialog* content_dialog)
+ : _content_dialog(content_dialog)
+ , _sizer(new wxBoxSizer(wxVERTICAL))
+ {
+ auto title = new wxBoxSizer(wxHORIZONTAL);
+ auto label = new wxStaticText(parent, wxID_ANY, wxEmptyString);
+ label->SetLabelMarkup(_("<b>Playlist:</b>"));
+ title->Add(label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, DCPOMATIC_SIZER_GAP);
+ _name = new wxTextCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400, -1));
+ title->Add(_name, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
_save_name = new Button(parent, _("Save"));
title->Add(_save_name);
- _sizer->Add (title, 0, wxTOP | wxLEFT, DCPOMATIC_SIZER_GAP * 2);
+ _sizer->Add(title, 0, wxTOP | wxLEFT, DCPOMATIC_SIZER_GAP * 2);
- auto list = new wxBoxSizer (wxHORIZONTAL);
+ auto list = new wxBoxSizer(wxHORIZONTAL);
- _list = new wxListCtrl (
+ _list = new wxListCtrl(
parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL
);
- _list->AppendColumn (_("Name"), wxLIST_FORMAT_LEFT, 400);
- _list->AppendColumn (_("CPL"), wxLIST_FORMAT_LEFT, 350);
- _list->AppendColumn (_("Type"), wxLIST_FORMAT_LEFT, 100);
- _list->AppendColumn (_("Encrypted"), wxLIST_FORMAT_CENTRE, 90);
+ _list->AppendColumn(_("Name"), wxLIST_FORMAT_LEFT, 400);
+ _list->AppendColumn(_("CPL"), wxLIST_FORMAT_LEFT, 350);
+ _list->AppendColumn(_("Type"), wxLIST_FORMAT_LEFT, 100);
+ _list->AppendColumn(_("Encrypted"), wxLIST_FORMAT_CENTRE, 90);
list->Add (_list, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
@@ -358,22 +356,27 @@ public:
return _sizer;
}
- void set (shared_ptr<SignalSPL> playlist)
+ void set(pair<ShowPlaylistID, ShowPlaylist> playlist)
{
_playlist = playlist;
- _list->DeleteAllItems ();
+ _list->DeleteAllItems();
+
+ ShowPlaylistList spl_list;
+ ShowPlaylistContentStore content;
if (_playlist) {
- for (auto i: _playlist->get()) {
- add (i);
+ for (auto uuid: spl_list.entries(_playlist->second.uuid())) {
+ if (auto entry = content.get(uuid)) {
+ add(*entry);
+ }
}
- _name->SetValue (std_to_wx(_playlist->name()));
+ _name->SetValue(std_to_wx(_playlist->second.name()));
} else {
- _name->SetValue({});
+ _name->SetValue(wxT(""));
}
- setup_sensitivity ();
+ setup_sensitivity();
}
- shared_ptr<SignalSPL> playlist () const
+ optional<pair<ShowPlaylistID, ShowPlaylist>> playlist() const
{
return _playlist;
}
@@ -382,11 +385,12 @@ public:
private:
void save_name_clicked()
{
- if (_playlist) {
- _playlist->set_name(wx_to_std(_name->GetValue()));
- save_playlist(_playlist);
- }
- setup_sensitivity();
+ // XXX
+ // if (_playlist) {
+ // _playlist->set_name(wx_to_std(_name->GetValue()));
+ // save_playlist(_playlist);
+ // }
+ // setup_sensitivity();
}
void name_changed ()
@@ -394,20 +398,20 @@ private:
setup_sensitivity();
}
- void add (SPLEntry e)
+ void add(ShowPlaylistEntry e)
{
wxListItem item;
- item.SetId (_list->GetItemCount());
- long const N = _list->InsertItem (item);
- set_item (N, e);
+ item.SetId(_list->GetItemCount());
+ long const N = _list->InsertItem(item);
+ set_item(N, e);
}
- void set_item (long N, SPLEntry e)
+ void set_item(long N, ShowPlaylistEntry e)
{
- _list->SetItem (N, 0, std_to_wx(e.name));
- _list->SetItem (N, 1, std_to_wx(e.id));
- _list->SetItem (N, 2, std_to_wx(e.kind->name()));
- _list->SetItem (N, 3, e.encrypted ? S_("Question|Y") : S_("Question|N"));
+ _list->SetItem(N, 0, std_to_wx(e.name()));
+ _list->SetItem(N, 1, std_to_wx(e.uuid()));
+ _list->SetItem(N, 2, std_to_wx(e.kind().name()));
+ _list->SetItem(N, 3, e.encrypted() ? S_("Question|Y") : S_("Question|N"));
}
void setup_sensitivity ()
@@ -416,7 +420,7 @@ private:
int const num_selected = _list->GetSelectedItemCount ();
long int selected = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
_name->Enable (have_list);
- _save_name->Enable(_playlist && _playlist->name() != wx_to_std(_name->GetValue()));
+ // _save_name->Enable(_playlist && _playlist->name() != wx_to_std(_name->GetValue()));
_list->Enable (have_list);
_up->Enable (have_list && selected > 0);
_down->Enable (have_list && selected != -1 && selected < (_list->GetItemCount() - 1));
@@ -426,58 +430,68 @@ private:
void add_clicked ()
{
- int const r = _content_dialog->ShowModal ();
+ auto const r = _content_dialog->ShowModal();
if (r == wxID_OK) {
- auto content = _content_dialog->selected ();
- if (content) {
- SPLEntry e (content);
- add (e);
- DCPOMATIC_ASSERT (_playlist);
- _playlist->add (e);
+ if (auto entry = _content_dialog->selected()) {
+ add(*entry);
+ DCPOMATIC_ASSERT(_playlist);
+ ShowPlaylistList spl_list;
+ spl_list.add_entry(_playlist->first, *entry);
}
}
}
void up_clicked ()
{
- long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ auto s = _list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (s < 1) {
return;
}
- DCPOMATIC_ASSERT (_playlist);
+ DCPOMATIC_ASSERT(_playlist);
+
+ auto content = ShowPlaylistContentStore::instance();
- _playlist->swap(s, s - 1);
+ ShowPlaylistList spl_list;
+ spl_list.move_entry_up(_playlist->first, s);
+ auto entries = spl_list.entries(_playlist->first);
+ std::cout << "UP!!!!!\n";
+ for (auto i: entries) {
+ std::cout << "E:" << i << " " << content->get(i)->name() << "\n";
+ }
- set_item (s - 1, (*_playlist)[s-1]);
- set_item (s, (*_playlist)[s]);
+ std::cout << "let's get it the fucker " << entries[s-1] << " " << content->get(entries[s])->name() << "\n";
+ set_item(s - 1, *content->get(entries[s]));
+ set_item(s, *content->get(entries[s - 1]));
}
void down_clicked ()
{
- long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
- if (s > (_list->GetItemCount() - 1)) {
- return;
- }
+ // XXX
+ // long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ // if (s > (_list->GetItemCount() - 1)) {
+ // return;
+ // }
- DCPOMATIC_ASSERT (_playlist);
+ // DCPOMATIC_ASSERT (_playlist);
- _playlist->swap(s, s + 1);
+ // _playlist->swap(s, s + 1);
- set_item (s + 1, (*_playlist)[s+1]);
- set_item (s, (*_playlist)[s]);
+ // set_item (s + 1, (*_playlist)[s+1]);
+ // set_item (s, (*_playlist)[s]);
}
void remove_clicked ()
{
- long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
- if (s == -1) {
- return;
- }
+ // XXX
+ // long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ // if (s == -1) {
+ // return;
+ // }
- DCPOMATIC_ASSERT (_playlist);
- _playlist->remove (s);
- _list->DeleteItem (s);
+ // DCPOMATIC_ASSERT (_playlist);
+ // _playlist->remove (s);
+ // _list->DeleteItem (s);
}
ContentDialog* _content_dialog;
@@ -489,7 +503,7 @@ private:
wxButton* _down;
wxButton* _add;
wxButton* _remove;
- shared_ptr<SignalSPL> _playlist;
+ optional<pair<ShowPlaylistID, ShowPlaylist>> _playlist;
};
@@ -519,7 +533,7 @@ public:
overall_panel->SetSizer (sizer);
- _playlist_list->Edit.connect (bind(&DOMFrame::change_playlist, this, _1));
+ _playlist_list->Edit.connect(bind(&DOMFrame::change_playlist, this, _1));
Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_exit, this), wxID_EXIT);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_about, this), wxID_ABOUT);
@@ -550,13 +564,11 @@ private:
_config_dialog->Show (this);
}
- void change_playlist (shared_ptr<SignalSPL> playlist)
+ void change_playlist(optional<pair<ShowPlaylistID, ShowPlaylist>> playlist)
{
- auto old = _playlist_content->playlist ();
- if (old) {
- save_playlist (old);
+ if (playlist) {
+ _playlist_content->set(*playlist);
}
- _playlist_content->set (playlist);
}
void setup_menu (wxMenuBar* m)
@@ -665,7 +677,7 @@ private:
*/
Config::drop ();
- update_content_store();
+ update_show_playlist_content_store();
_frame = new DOMFrame(variant::wx::dcpomatic_playlist_editor());
SetTopWindow (_frame);
@@ -679,7 +691,14 @@ private:
}
catch (exception& e)
{
- error_dialog(nullptr, variant::wx::insert_dcpomatic_playlist_editor(_("%s could not start %s")), std_to_wx(e.what()));
+ error_dialog(
+ nullptr,
+ wxString::Format(
+ wxT("%s: %s"),
+ variant::wx::insert_dcpomatic_playlist_editor(_("%s could not start")),
+ std_to_wx(e.what())
+ )
+ );
return true;
}
diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc
index 17ee0fb21..bca2e20b9 100644
--- a/src/wx/config_dialog.cc
+++ b/src/wx/config_dialog.cc
@@ -24,6 +24,7 @@
#include "check_box.h"
#include "config_dialog.h"
#include "dcpomatic_button.h"
+#include "file_picker_ctrl.h"
#include "nag_dialog.h"
#include "static_text.h"
#include "wx_variant.h"
@@ -1041,9 +1042,9 @@ LocationsPage::setup ()
table->Add (_content_directory, wxGBPosition (r, 1));
++r;
- add_label_to_sizer (table, _panel, _("Playlist directory"), true, wxGBPosition (r, 0));
- _playlist_directory = new wxDirPickerCtrl (_panel, wxID_ANY, wxEmptyString, char_to_wx(wxDirSelectorPromptStr), wxDefaultPosition, wxSize (300, -1));
- table->Add (_playlist_directory, wxGBPosition (r, 1));
+ add_label_to_sizer(table, _panel, _("Show playlists file"), true, wxGBPosition (r, 0));
+ _show_playlists_file = new FilePickerCtrl(_panel, _("Select show playlists file"), _("SQLite3 files (.sqlite3)|*.sqlite3)"), false, true, "ShowPlaylistsFile");
+ table->Add(_show_playlists_file, wxGBPosition (r, 1));
++r;
add_label_to_sizer (table, _panel, _("KDM directory"), true, wxGBPosition (r, 0));
@@ -1052,7 +1053,7 @@ LocationsPage::setup ()
++r;
_content_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::content_directory_changed, this));
- _playlist_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::playlist_directory_changed, this));
+ _show_playlists_file->Bind(wxEVT_FILEPICKER_CHANGED, bind(&LocationsPage::show_playlists_file_changed, this));
_kdm_directory->Bind (wxEVT_DIRPICKER_CHANGED, bind(&LocationsPage::kdm_directory_changed, this));
}
@@ -1064,9 +1065,7 @@ LocationsPage::config_changed ()
if (config->player_content_directory()) {
checked_set (_content_directory, *config->player_content_directory());
}
- if (config->player_playlist_directory()) {
- checked_set (_playlist_directory, *config->player_playlist_directory());
- }
+ checked_set(_show_playlists_file, config->show_playlists_file());
if (config->player_kdm_directory()) {
checked_set (_kdm_directory, *config->player_kdm_directory());
}
@@ -1079,9 +1078,11 @@ LocationsPage::content_directory_changed ()
}
void
-LocationsPage::playlist_directory_changed ()
+LocationsPage::show_playlists_file_changed()
{
- Config::instance()->set_player_playlist_directory(wx_to_std(_playlist_directory->GetPath()));
+ if (auto path = _show_playlists_file->path()) {
+ Config::instance()->set_show_playlists_file(*path);
+ }
}
void
diff --git a/src/wx/config_dialog.h b/src/wx/config_dialog.h
index ce2686864..62cee0145 100644
--- a/src/wx/config_dialog.h
+++ b/src/wx/config_dialog.h
@@ -235,11 +235,11 @@ private:
void setup () override;
void config_changed () override;
void content_directory_changed ();
- void playlist_directory_changed ();
+ void show_playlists_file_changed();
void kdm_directory_changed ();
wxDirPickerCtrl* _content_directory;
- wxDirPickerCtrl* _playlist_directory;
+ FilePickerCtrl* _show_playlists_file;
wxDirPickerCtrl* _kdm_directory;
};
diff --git a/src/wx/content_view.cc b/src/wx/content_view.cc
index f3c11fa04..46da832a0 100644
--- a/src/wx/content_view.cc
+++ b/src/wx/content_view.cc
@@ -62,55 +62,50 @@ ContentView::ContentView (wxWindow* parent)
}
-shared_ptr<Content>
-ContentView::selected () const
+optional<ShowPlaylistEntry>
+ContentView::selected() const
{
- long int s = GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ long int s = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (s == -1) {
return {};
}
- DCPOMATIC_ASSERT(s < int(_content_digests.size()));
- return ContentStore::instance()->get(_content_digests[s]);
+ DCPOMATIC_ASSERT(s < int(_uuids.size()));
+ return ShowPlaylistContentStore::instance()->get(_uuids[s]);
}
void
-ContentView::update ()
+ContentView::update()
{
- DeleteAllItems ();
- _content_digests.clear();
- for (auto content: ContentStore::instance()->all()) {
- add(content);
+ DeleteAllItems();
+ _uuids.clear();
+ for (auto entry: ShowPlaylistContentStore::instance()->all()) {
+ add(entry);
}
}
void
-ContentView::add(shared_ptr<Content> content)
+ContentView::add(ShowPlaylistEntry entry)
{
int const N = GetItemCount();
wxListItem it;
it.SetId(N);
it.SetColumn(0);
- auto length = content->approximate_length ();
- auto const hmsf = length.split (24);
- it.SetText(wxString::Format(char_to_wx("%02d:%02d:%02d"), hmsf.h, hmsf.m, hmsf.s));
+ it.SetText(std_to_wx(entry.approximate_length()));
InsertItem(it);
- auto dcp = dynamic_pointer_cast<DCPContent>(content);
- if (dcp && dcp->content_kind()) {
- it.SetId(N);
- it.SetColumn(1);
- it.SetText(std_to_wx(dcp->content_kind()->name()));
- SetItem(it);
- }
+ it.SetId(N);
+ it.SetColumn(1);
+ it.SetText(std_to_wx(entry.kind().name()));
+ SetItem(it);
it.SetId(N);
it.SetColumn(2);
- it.SetText(std_to_wx(content->summary()));
+ it.SetText(std_to_wx(entry.name()));
SetItem(it);
- _content_digests.push_back(content->digest());
+ _uuids.push_back(entry.uuid());
}
diff --git a/src/wx/content_view.h b/src/wx/content_view.h
index 65f806340..e27b14132 100644
--- a/src/wx/content_view.h
+++ b/src/wx/content_view.h
@@ -19,7 +19,7 @@
*/
-#include "lib/content_store.h"
+#include "lib/show_playlist_content_store.h"
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
#include <wx/listctrl.h>
@@ -31,17 +31,21 @@ class Content;
class Film;
+/** @class ContentView
+ *
+ * @brief A GUI list of content from our ShowPlaylistContentStore.
+ */
class ContentView : public wxListCtrl
{
public:
ContentView (wxWindow* parent);
- std::shared_ptr<Content> selected () const;
+ boost::optional<ShowPlaylistEntry> selected() const;
void update ();
private:
- void add (std::shared_ptr<Content> content);
+ void add(ShowPlaylistEntry entry);
std::weak_ptr<Film> _film;
- std::vector<std::string> _content_digests;
+ std::vector<std::string> _uuids;
};
diff --git a/src/wx/playlist_controls.cc b/src/wx/playlist_controls.cc
index 0f88f2881..488215fe9 100644
--- a/src/wx/playlist_controls.cc
+++ b/src/wx/playlist_controls.cc
@@ -35,6 +35,7 @@
#include "lib/internet.h"
#include "lib/player_video.h"
#include "lib/scoped_temporary.h"
+#include "lib/show_playlist_list.h"
#include <dcp/exceptions.h>
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
@@ -118,10 +119,10 @@ PlaylistControls::PlaylistControls(wxWindow* parent, FilmViewer& viewer)
_spl_view->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&PlaylistControls::spl_selection_changed, this));
_spl_view->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&PlaylistControls::spl_selection_changed, this));
_viewer.Finished.connect(boost::bind(&PlaylistControls::viewer_finished, this));
- _refresh_spl_view->Bind (wxEVT_BUTTON, boost::bind(&PlaylistControls::update_playlist_directory, this));
+ _refresh_spl_view->Bind (wxEVT_BUTTON, boost::bind(&PlaylistControls::update_playlists, this));
_refresh_content_view->Bind (wxEVT_BUTTON, boost::bind(&ContentView::update, _content_view));
- update_playlist_directory ();
+ update_playlists();
}
void
@@ -209,7 +210,9 @@ PlaylistControls::previous_clicked ()
bool
PlaylistControls::can_do_next ()
{
- return _selected_playlist && (_selected_playlist_position + 1) < int(_playlists[*_selected_playlist].get().size());
+ ShowPlaylistList spl_list;
+ auto entries = spl_list.entries(_playlists[*_selected_playlist].uuid());
+ return _selected_playlist && (_selected_playlist_position + 1) < int(entries.size());
}
void
@@ -225,61 +228,44 @@ PlaylistControls::next_clicked ()
void
-PlaylistControls::add_playlist_to_list (SPL spl)
+PlaylistControls::add_playlist_to_list(ShowPlaylist show_playlist)
{
int const N = _spl_view->GetItemCount();
wxListItem it;
it.SetId(N);
it.SetColumn(0);
- string t = spl.name();
- if (spl.missing()) {
- t += " (content missing)";
- }
+ string t = show_playlist.name();
+ // XXX
+ // if (spl.missing()) {
+ // t += " (content missing)";
+ // }
it.SetText (std_to_wx(t));
_spl_view->InsertItem (it);
}
-struct SPLComparator
-{
- bool operator() (SPL const & a, SPL const & b) {
- return a.name() < b.name();
- }
-};
void
-PlaylistControls::update_playlist_directory ()
+PlaylistControls::update_playlists()
{
using namespace boost::filesystem;
_spl_view->DeleteAllItems ();
- optional<path> dir = Config::instance()->player_playlist_directory();
- if (!dir) {
- return;
- }
+ _playlists.clear();
- _playlists.clear ();
-
- for (directory_iterator i = directory_iterator(*dir); i != directory_iterator(); ++i) {
- try {
- if (is_regular_file(i->path()) && i->path().extension() == ".xml") {
- SPL spl;
- spl.read(i->path(), ContentStore::instance());
- _playlists.push_back (spl);
- }
- } catch (exception& e) {
- /* Never mind */
- }
+ ShowPlaylistList spl_list;
+ for (auto const& spl: spl_list.show_playlists()) {
+ _playlists.push_back(spl.second);
}
- sort (_playlists.begin(), _playlists.end(), SPLComparator());
for (auto i: _playlists) {
- add_playlist_to_list (i);
+ add_playlist_to_list(i);
}
_selected_playlist = boost::none;
}
+
optional<dcp::EncryptedKDM>
PlaylistControls::get_kdm_from_directory (shared_ptr<DCPContent> dcp)
{
@@ -313,13 +299,17 @@ PlaylistControls::spl_selection_changed ()
return;
}
- if (_playlists[selected].missing()) {
- error_dialog(this, _("This playlist cannot be loaded as some content is missing."));
- deselect_playlist ();
- return;
- }
+ // XXX
+ // if (_playlists[selected].missing()) {
+ // error_dialog(this, _("This playlist cannot be loaded as some content is missing."));
+ // deselect_playlist ();
+ // return;
+ // }
+
+ ShowPlaylistList spl_list;
+ auto const entries = spl_list.entries(_playlists[selected].uuid());
- if (_playlists[selected].get().empty()) {
+ if (entries.empty()) {
error_dialog(this, _("This playlist is empty."));
return;
}
@@ -332,13 +322,20 @@ PlaylistControls::select_playlist (int selected, int position)
{
wxProgressDialog dialog(variant::wx::dcpomatic(), _("Loading playlist and KDMs"));
- for (auto const& i: _playlists[selected].get()) {
+ _playlist.clear();
+
+ ShowPlaylistList spl_list;
+ for (auto const& uuid: spl_list.entries(_playlists[selected].uuid())) {
dialog.Pulse ();
- shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (i.content);
+
+ auto entry = ShowPlaylistContentStore::instance()->get(uuid);
+ if (!entry) {
+ continue;
+ }
+
+ auto dcp = dynamic_pointer_cast<DCPContent>(entry->content());
if (dcp && dcp->needs_kdm()) {
- optional<dcp::EncryptedKDM> kdm;
- kdm = get_kdm_from_directory (dcp);
- if (kdm) {
+ if (auto kdm = get_kdm_from_directory(dcp)) {
try {
dcp->add_kdm (*kdm);
dcp->examine(_film, shared_ptr<Job>(), true);
@@ -358,13 +355,15 @@ PlaylistControls::select_playlist (int selected, int position)
_current_spl_view->DeleteAllItems ();
int N = 0;
- for (auto i: _playlists[selected].get()) {
- wxListItem it;
- it.SetId (N);
- it.SetColumn (0);
- it.SetText (std_to_wx(i.name));
- _current_spl_view->InsertItem (it);
- ++N;
+ for (auto const& uuid: spl_list.entries(_playlists[selected].uuid())) {
+ if (auto entry = ShowPlaylistContentStore::instance()->get(uuid)) {
+ wxListItem it;
+ it.SetId(N);
+ it.SetColumn(0);
+ it.SetText(std_to_wx(entry->name()));
+ _current_spl_view->InsertItem(it);
+ ++N;
+ }
}
_selected_playlist = selected;
@@ -379,9 +378,13 @@ void
PlaylistControls::reset_film ()
{
DCPOMATIC_ASSERT (_selected_playlist);
- shared_ptr<Film> film (new Film(optional<boost::filesystem::path>()));
- film->add_content (_playlists[*_selected_playlist].get()[_selected_playlist_position].content);
- ResetFilm (film);
+ auto film = std::make_shared<Film>(optional<boost::filesystem::path>());
+ ShowPlaylistList spl_list;
+ auto uuid = spl_list.entries(_playlists[*_selected_playlist].uuid())[_selected_playlist_position];
+ if (auto entry = ShowPlaylistContentStore::instance()->get(uuid)) {
+ film->add_content(entry->content());
+ }
+ ResetFilm(film);
}
void
@@ -391,8 +394,8 @@ PlaylistControls::config_changed (int property)
if (property == Config::PLAYER_CONTENT_DIRECTORY) {
_content_view->update ();
- } else if (property == Config::PLAYER_PLAYLIST_DIRECTORY) {
- update_playlist_directory ();
+ } else if (property == Config::SHOW_PLAYLISTS_FILE) {
+ update_playlists();
}
}
@@ -417,8 +420,11 @@ PlaylistControls::viewer_finished ()
return;
}
+ ShowPlaylistList spl_list;
+ auto entries = spl_list.entries(_playlists[*_selected_playlist].uuid());
+
_selected_playlist_position++;
- if (_selected_playlist_position < int(_playlists[*_selected_playlist].get().size())) {
+ if (_selected_playlist_position < int(entries.size())) {
/* Next piece of content on the SPL */
update_current_content ();
_viewer.start();
diff --git a/src/wx/playlist_controls.h b/src/wx/playlist_controls.h
index 76ec63824..a9516ef3c 100644
--- a/src/wx/playlist_controls.h
+++ b/src/wx/playlist_controls.h
@@ -20,7 +20,7 @@
#include "controls.h"
-#include "lib/spl.h"
+#include "lib/show_playlist.h"
class DCPContent;
@@ -46,9 +46,9 @@ private:
void stop_clicked ();
void next_clicked ();
void previous_clicked ();
- void add_playlist_to_list (SPL spl);
+ void add_playlist_to_list(ShowPlaylist spl);
void update_content_directory ();
- void update_playlist_directory ();
+ void update_playlists();
void spl_selection_changed ();
void select_playlist (int selected, int position);
void started () override;
@@ -76,7 +76,8 @@ private:
wxButton* _refresh_spl_view;
wxListCtrl* _current_spl_view;
- std::vector<SPL> _playlists;
+ std::vector<ShowPlaylist> _playlists;
boost::optional<int> _selected_playlist;
+ std::vector<std::shared_ptr<Content>> _playlist;
int _selected_playlist_position;
};
diff --git a/src/wx/wx_util.cc b/src/wx/wx_util.cc
index bc2d593f1..cb231eb5f 100644
--- a/src/wx/wx_util.cc
+++ b/src/wx/wx_util.cc
@@ -33,7 +33,7 @@
#include "wx_util.h"
#include "wx_variant.h"
#include "lib/config.h"
-#include "lib/content_store.h"
+#include "lib/show_playlist_content_store.h"
#include "lib/cross.h"
#include "lib/job.h"
#include "lib/job_manager.h"
@@ -845,7 +845,7 @@ layout_for_short_screen(wxWindow* reference)
void
-update_content_store()
+update_show_playlist_content_store()
{
auto dir = Config::instance()->player_content_directory();
if (!dir || !dcp::filesystem::is_directory(*dir)) {
@@ -854,7 +854,7 @@ update_content_store()
wxProgressDialog progress(variant::wx::dcpomatic(), _("Reading content directory"));
- auto store = ContentStore::instance();
+ auto store = ShowPlaylistContentStore::instance();
auto errors = store->update([&progress]() {
return progress.Pulse();
diff --git a/src/wx/wx_util.h b/src/wx/wx_util.h
index 6c3f12ef2..3f513f0a3 100644
--- a/src/wx/wx_util.h
+++ b/src/wx/wx_util.h
@@ -126,7 +126,7 @@ extern double dpi_scale_factor (wxWindow* window);
extern int search_ctrl_height ();
extern void report_config_load_failure(wxWindow* parent, Config::LoadFailure what);
extern bool layout_for_short_screen(wxWindow* reference);
-extern void update_content_store();
+extern void update_show_playlist_content_store();
struct Offset