summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-04-29 00:47:47 +0200
committerCarl Hetherington <cth@carlh.net>2025-06-03 22:46:26 +0200
commitfb5b8ae43e373c37b78202217528be73efa162cc (patch)
tree9dfb23d6188ba86485795eb13876ebc01f560874 /src
parenteb2452d1d76e7b87a61e88773b29206ff5833e34 (diff)
Allow multiple upload destinations instead of just the TMS.
Diffstat (limited to 'src')
-rw-r--r--src/lib/config.cc73
-rw-r--r--src/lib/config.h77
-rw-r--r--src/lib/curl_uploader.cc12
-rw-r--r--src/lib/curl_uploader.h2
-rw-r--r--src/lib/dcp_transcode_job.cc13
-rw-r--r--src/lib/film.cc3
-rw-r--r--src/lib/scp_uploader.cc14
-rw-r--r--src/lib/scp_uploader.h2
-rw-r--r--src/lib/upload_destination.cc62
-rw-r--r--src/lib/upload_destination.h69
-rw-r--r--src/lib/upload_job.cc15
-rw-r--r--src/lib/upload_job.h5
-rw-r--r--src/lib/uploader.cc3
-rw-r--r--src/lib/uploader.h4
-rw-r--r--src/lib/wscript1
-rw-r--r--src/wx/full_config_dialog.cc126
-rw-r--r--src/wx/upload_destination_dialog.cc105
-rw-r--r--src/wx/upload_destination_dialog.h51
-rw-r--r--src/wx/wscript1
19 files changed, 413 insertions, 225 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc
index 66a0e1a5a..51246981f 100644
--- a/src/lib/config.cc
+++ b/src/lib/config.cc
@@ -69,7 +69,7 @@ using dcp::raw_convert;
Config* Config::_instance = 0;
-int const Config::_current_version = 3;
+int const Config::_current_version = 4;
boost::signals2::signal<void (Config::LoadFailure)> Config::FailedToLoad;
boost::signals2::signal<void (string)> Config::Warning;
boost::signals2::signal<bool (Config::BadReason)> Config::Bad;
@@ -98,12 +98,8 @@ Config::set_defaults()
_use_any_servers = true;
_servers.clear();
_only_servers_encode = false;
- _tms_protocol = FileTransferProtocol::SCP;
- _tms_passive = true;
- _tms_ip = "";
- _tms_path = ".";
- _tms_user = "";
- _tms_password = "";
+ _upload_destinations.clear();
+ _upload_after_make_dcp.clear();
_allow_any_dcp_frame_rate = false;
_allow_any_container = false;
_allow_96khz_audio = false;
@@ -118,7 +114,6 @@ Config::set_defaults()
_default_audio_delay = 0;
_default_interop = false;
_default_metadata.clear();
- _upload_after_make_dcp = false;
_mail_server = "";
_mail_port = 25;
_mail_protocol = EmailProtocol::AUTO;
@@ -349,12 +344,34 @@ try
}
_only_servers_encode = f.optional_bool_child("OnlyServersEncode").get_value_or(false);
- _tms_protocol = static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP)));
- _tms_passive = f.optional_bool_child("TMSPassive").get_value_or(true);
- _tms_ip = f.string_child("TMSIP");
- _tms_path = f.string_child("TMSPath");
- _tms_user = f.string_child("TMSUser");
- _tms_password = f.string_child("TMSPassword");
+
+ if (f.optional_string_child("TMSIP")) {
+ _upload_destinations.emplace_back(
+ _("TMS"),
+ static_cast<FileTransferProtocol>(f.optional_number_child<int>("TMSProtocol").get_value_or(static_cast<int>(FileTransferProtocol::SCP))),
+ f.optional_bool_child("TMSPassive").get_value_or(true),
+ f.string_child("TMSIP"),
+ f.string_child("TMSPath"),
+ f.string_child("TMSUser"),
+ f.string_child("TMSPassword")
+ );
+ }
+
+ auto up = f.optional_bool_child("UploadAfterMakeDCP");
+ if (!up) {
+ up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
+ }
+ if (up && *up) {
+ _upload_after_make_dcp = { _("TMS") };
+ }
+
+ for (auto i: f.node_children("UploadDestination")) {
+ _upload_destinations.push_back(UploadDestination(i));
+ }
+
+ for (auto i: f.node_children("UploadAfterMakeDCP")) {
+ _upload_after_make_dcp.push_back(i->content());
+ }
_language = f.optional_string_child("Language");
@@ -367,11 +384,6 @@ try
_dcp_issuer = f.string_child("DCPIssuer");
}
- auto up = f.optional_bool_child("UploadAfterMakeDCP");
- if (!up) {
- up = f.optional_bool_child("DefaultUploadAfterMakeDCP");
- }
- _upload_after_make_dcp = up.get_value_or(false);
_dcp_creator = f.optional_string_child("DCPCreator").get_value_or("");
_dcp_company_name = f.optional_string_child("DCPCompanyName").get_value_or("");
_dcp_product_name = f.optional_string_child("DCPProductName").get_value_or("");
@@ -776,18 +788,15 @@ Config::write_config() const
is done by the encoding servers. 0 to set the master to do some encoding as well as coordinating the job.
*/
cxml::add_text_child(root, "OnlyServersEncode", _only_servers_encode ? "1" : "0");
- /* [XML] TMSProtocol Protocol to use to copy files to a TMS; 0 to use SCP, 1 for FTP. */
- cxml::add_text_child(root, "TMSProtocol", fmt::to_string(static_cast<int>(_tms_protocol)));
- /* [XML] TMSPassive True to use PASV mode with TMS FTP connections. */
- cxml::add_text_child(root, "TMSPassive", _tms_passive ? "1" : "0");
- /* [XML] TMSIP IP address of TMS. */
- cxml::add_text_child(root, "TMSIP", _tms_ip);
- /* [XML] TMSPath Path on the TMS to copy files to. */
- cxml::add_text_child(root, "TMSPath", _tms_path);
- /* [XML] TMSUser Username to log into the TMS with. */
- cxml::add_text_child(root, "TMSUser", _tms_user);
- /* [XML] TMSPassword Password to log into the TMS with. */
- cxml::add_text_child(root, "TMSPassword", _tms_password);
+
+ for (auto i: _upload_destinations) {
+ i.as_xml(cxml::add_child(root, "UploadDestination"));
+ }
+
+ for (auto i: _upload_after_make_dcp) {
+ cxml::add_text_child(root, "UploadAfterMakeDCP", i);
+ }
+
if (_language) {
/* [XML:opt] Language Language to use in the GUI e.g. <code>fr_FR</code>. */
cxml::add_text_child(root, "Language", _language.get());
@@ -804,8 +813,6 @@ Config::write_config() const
cxml::add_text_child(root, "DCPProductVersion", _dcp_product_version);
/* [XML] DCPJ2KComment Comment to write into JPEG2000 data. */
cxml::add_text_child(root, "DCPJ2KComment", _dcp_j2k_comment);
- /* [XML] UploadAfterMakeDCP 1 to upload to a TMS after making a DCP, 0 for no upload. */
- cxml::add_text_child(root, "UploadAfterMakeDCP", _upload_after_make_dcp ? "1" : "0");
/* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */
cxml::add_text_child(root, "DefaultStillLength", fmt::to_string(_default_still_length));
diff --git a/src/lib/config.h b/src/lib/config.h
index c90790ebc..77f97e151 100644
--- a/src/lib/config.h
+++ b/src/lib/config.h
@@ -32,6 +32,7 @@
#include "export_config.h"
#include "rough_duration.h"
#include "state.h"
+#include "upload_destination.h"
#include "video_encoding.h"
#include <dcp/name_format.h>
#include <dcp/certificate_chain.h>
@@ -154,32 +155,8 @@ public:
return _only_servers_encode;
}
- FileTransferProtocol tms_protocol() const {
- return _tms_protocol;
- }
-
- bool tms_passive() const {
- return _tms_passive;
- }
-
- /** @return The IP address of a TMS that we can copy DCPs to */
- std::string tms_ip() const {
- return _tms_ip;
- }
-
- /** @return The path on a TMS that we should changed DCPs to */
- std::string tms_path() const {
- return _tms_path;
- }
-
- /** @return User name to log into the TMS with */
- std::string tms_user() const {
- return _tms_user;
- }
-
- /** @return Password to log into the TMS with */
- std::string tms_password() const {
- return _tms_password;
+ std::vector<UploadDestination> upload_destinations() const {
+ return _upload_destinations;
}
std::list<int> allowed_dcp_frame_rates() const {
@@ -270,7 +247,7 @@ public:
return _default_metadata;
}
- bool upload_after_make_dcp() {
+ std::vector<std::string> upload_after_make_dcp() {
return _upload_after_make_dcp;
}
@@ -716,32 +693,8 @@ public:
maybe_set(_only_servers_encode, o);
}
- void set_tms_protocol(FileTransferProtocol p) {
- maybe_set(_tms_protocol, p);
- }
-
- void set_tms_passive(bool passive) {
- maybe_set(_tms_passive, passive);
- }
-
- /** @param i IP address of a TMS that we can copy DCPs to */
- void set_tms_ip(std::string i) {
- maybe_set(_tms_ip, i);
- }
-
- /** @param p Path on a TMS that we should changed DCPs to */
- void set_tms_path(std::string p) {
- maybe_set(_tms_path, p);
- }
-
- /** @param u User name to log into the TMS with */
- void set_tms_user(std::string u) {
- maybe_set(_tms_user, u);
- }
-
- /** @param p Password to log into the TMS with */
- void set_tms_password(std::string p) {
- maybe_set(_tms_password, p);
+ void set_upload_destinations(std::vector<UploadDestination> destinations) {
+ maybe_set(_upload_destinations, destinations);
}
void set_allowed_dcp_frame_rates(std::list<int> const & r) {
@@ -825,8 +778,8 @@ public:
maybe_set(_default_audio_language, boost::optional<dcp::LanguageTag>());
}
- void set_upload_after_make_dcp(bool u) {
- maybe_set(_upload_after_make_dcp, u);
+ void set_upload_after_make_dcp(std::vector<std::string> names) {
+ maybe_set(_upload_after_make_dcp, std::move(names));
}
void set_mail_server(std::string s) {
@@ -1348,16 +1301,9 @@ private:
/** J2K encoding servers that should definitely be used */
std::vector<std::string> _servers;
bool _only_servers_encode;
- FileTransferProtocol _tms_protocol;
- bool _tms_passive;
- /** The IP address of a TMS that we can copy DCPs to */
- std::string _tms_ip;
- /** The path on a TMS that we should write DCPs to */
- std::string _tms_path;
- /** User name to log into the TMS with */
- std::string _tms_user;
- /** Password to log into the TMS with */
- std::string _tms_password;
+ std::vector<UploadDestination> _upload_destinations;
+ /** Names of UploadDestinations to automatically upload to after making a DCP */
+ std::vector<std::string> _upload_after_make_dcp;
/** The list of possible DCP frame rates that DCP-o-matic will use */
std::list<int> _allowed_dcp_frame_rates;
/** Allow any video frame rate for the DCP; if true, overrides _allowed_dcp_frame_rates */
@@ -1392,7 +1338,6 @@ private:
the home directory will be offered.
*/
boost::optional<boost::filesystem::path> _default_kdm_directory;
- bool _upload_after_make_dcp;
std::string _mail_server;
int _mail_port;
EmailProtocol _mail_protocol;
diff --git a/src/lib/curl_uploader.cc b/src/lib/curl_uploader.cc
index 753c66496..fa18f2963 100644
--- a/src/lib/curl_uploader.cc
+++ b/src/lib/curl_uploader.cc
@@ -51,8 +51,8 @@ curl_debug_shim(CURL* curl, curl_infotype type, char* data, size_t size, void* u
}
-CurlUploader::CurlUploader(function<void (string)> set_status, function<void (float)> set_progress)
- : Uploader(set_status, set_progress)
+CurlUploader::CurlUploader(UploadDestination destination, function<void (string)> set_status, function<void (float)> set_progress)
+ : Uploader(destination, set_status, set_progress)
{
_curl = curl_easy_init();
if (!_curl) {
@@ -64,9 +64,9 @@ CurlUploader::CurlUploader(function<void (string)> set_status, function<void (fl
curl_easy_setopt(_curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(_curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L);
curl_easy_setopt(_curl, CURLOPT_READDATA, this);
- curl_easy_setopt(_curl, CURLOPT_USERNAME, Config::instance()->tms_user().c_str());
- curl_easy_setopt(_curl, CURLOPT_PASSWORD, Config::instance()->tms_password().c_str());
- if (!Config::instance()->tms_passive()) {
+ curl_easy_setopt(_curl, CURLOPT_USERNAME, destination.user.c_str());
+ curl_easy_setopt(_curl, CURLOPT_PASSWORD, destination.password.c_str());
+ if (!destination.passive_ftp) {
curl_easy_setopt(_curl, CURLOPT_FTPPORT, "-");
}
curl_easy_setopt(_curl, CURLOPT_VERBOSE, 1L);
@@ -94,7 +94,7 @@ CurlUploader::upload_file(boost::filesystem::path from, boost::filesystem::path
curl_easy_setopt(
_curl, CURLOPT_URL,
/* Use generic_string so that we get forward-slashes in the path, even on Windows */
- String::compose("ftp://%1/%2/%3", Config::instance()->tms_ip(), Config::instance()->tms_path(), to.generic_string()).c_str()
+ String::compose("ftp://%1/%2/%3", _destination.host, _destination.path, to.generic_string()).c_str()
);
dcp::File file(from, "rb");
diff --git a/src/lib/curl_uploader.h b/src/lib/curl_uploader.h
index 0634d564f..0562a3a59 100644
--- a/src/lib/curl_uploader.h
+++ b/src/lib/curl_uploader.h
@@ -27,7 +27,7 @@
class CurlUploader : public Uploader
{
public:
- CurlUploader(std::function<void (std::string)> set_status, std::function<void (float)> set_progress);
+ CurlUploader(UploadDestination destination, std::function<void (std::string)> set_status, std::function<void (float)> set_progress);
~CurlUploader();
size_t read_callback(void* ptr, size_t size, size_t nmemb);
diff --git a/src/lib/dcp_transcode_job.cc b/src/lib/dcp_transcode_job.cc
index dd7b7d624..0f1248d99 100644
--- a/src/lib/dcp_transcode_job.cc
+++ b/src/lib/dcp_transcode_job.cc
@@ -46,8 +46,17 @@ DCPTranscodeJob::DCPTranscodeJob (shared_ptr<const Film> film, ChangedBehaviour
void
DCPTranscodeJob::post_transcode ()
{
- if (Config::instance()->upload_after_make_dcp()) {
- JobManager::instance()->add(make_shared<UploadJob>(_film));
+ auto destinations = Config::instance()->upload_destinations();
+ for (auto i: Config::instance()->upload_after_make_dcp()) {
+ auto iter = std::find_if(
+ destinations.begin(),
+ destinations.end(),
+ [i](UploadDestination const& destination) { return destination.name == i; }
+ );
+
+ if (iter != destinations.end()) {
+ JobManager::instance()->add(make_shared<UploadJob>(_film, *iter));
+ }
}
/* The first directory is the project's DCP, so the first CPL will also be from the project
diff --git a/src/lib/film.cc b/src/lib/film.cc
index d0a26e44c..6dcc8bb75 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -382,7 +382,8 @@ Film::subtitle_analysis_path(shared_ptr<const Content> content) const
void
Film::send_dcp_to_tms()
{
- JobManager::instance()->add(make_shared<UploadJob>(shared_from_this()));
+ /* XXX */
+ // JobManager::instance()->add(make_shared<UploadJob>(shared_from_this()));
}
shared_ptr<xmlpp::Document>
diff --git a/src/lib/scp_uploader.cc b/src/lib/scp_uploader.cc
index 193894293..1665c7a53 100644
--- a/src/lib/scp_uploader.cc
+++ b/src/lib/scp_uploader.cc
@@ -39,22 +39,22 @@ using std::shared_ptr;
using std::string;
-SCPUploader::SCPUploader(function<void (string)> set_status, function<void (float)> set_progress)
- : Uploader(set_status, set_progress)
+SCPUploader::SCPUploader(UploadDestination destination, function<void (string)> set_status, function<void (float)> set_progress)
+ : Uploader(destination, set_status, set_progress)
{
_session = ssh_new();
if (!_session) {
throw NetworkError(String::compose(_("SSH error [%1]"), "ssh_new"));
}
- ssh_options_set(_session, SSH_OPTIONS_HOST, Config::instance()->tms_ip().c_str());
- ssh_options_set(_session, SSH_OPTIONS_USER, Config::instance()->tms_user().c_str());
+ ssh_options_set(_session, SSH_OPTIONS_HOST, _destination.host.c_str());
+ ssh_options_set(_session, SSH_OPTIONS_USER, _destination.user.c_str());
int const port = 22;
ssh_options_set(_session, SSH_OPTIONS_PORT, &port);
int r = ssh_connect(_session);
if (r != SSH_OK) {
- throw NetworkError(String::compose(_("Could not connect to server %1 (%2)"), Config::instance()->tms_ip(), ssh_get_error(_session)));
+ throw NetworkError(String::compose(_("Could not connect to server %1 (%2)"), _destination.host, ssh_get_error(_session)));
}
LIBDCP_DISABLE_WARNINGS
@@ -64,13 +64,13 @@ LIBDCP_DISABLE_WARNINGS
}
LIBDCP_ENABLE_WARNINGS
- r = ssh_userauth_password(_session, 0, Config::instance()->tms_password().c_str());
+ r = ssh_userauth_password(_session, 0, _destination.password.c_str());
if (r != SSH_AUTH_SUCCESS) {
throw NetworkError(String::compose(_("Failed to authenticate with server (%1)"), ssh_get_error(_session)));
}
LIBDCP_DISABLE_WARNINGS
- _scp = ssh_scp_new(_session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, Config::instance()->tms_path().c_str());
+ _scp = ssh_scp_new(_session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, _destination.path.c_str());
LIBDCP_ENABLE_WARNINGS
if (!_scp) {
throw NetworkError(String::compose(_("SSH error [%1] (%2)"), "ssh_scp_new", ssh_get_error(_session)));
diff --git a/src/lib/scp_uploader.h b/src/lib/scp_uploader.h
index bae175ea8..79501d3cf 100644
--- a/src/lib/scp_uploader.h
+++ b/src/lib/scp_uploader.h
@@ -29,7 +29,7 @@ LIBDCP_ENABLE_WARNINGS
class SCPUploader : public Uploader
{
public:
- SCPUploader(std::function<void (std::string)> set_status, std::function<void (float)> set_progress);
+ SCPUploader(UploadDestination destination, std::function<void (std::string)> set_status, std::function<void (float)> set_progress);
~SCPUploader();
protected:
diff --git a/src/lib/upload_destination.cc b/src/lib/upload_destination.cc
new file mode 100644
index 000000000..7acce968b
--- /dev/null
+++ b/src/lib/upload_destination.cc
@@ -0,0 +1,62 @@
+/*
+ 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 "upload_destination.h"
+#include <libcxml/cxml.h>
+
+
+UploadDestination::UploadDestination(cxml::ConstNodePtr node)
+ : name(node->string_child("Name"))
+ , protocol(node->string_child("Protocol") == "SCP" ? FileTransferProtocol::SCP : FileTransferProtocol::FTP)
+ , passive_ftp(node->bool_child("PassiveFTP"))
+ , host(node->string_child("Host"))
+ , path(node->string_child("Path"))
+ , user(node->string_child("User"))
+ , password(node->string_child("Password"))
+{
+
+}
+
+
+void
+UploadDestination::as_xml(xmlpp::Element* parent) const
+{
+ cxml::add_text_child(parent, "Name", name);
+ cxml::add_text_child(parent, "Protocol", protocol == FileTransferProtocol::SCP ? "SCP" : "FTP");
+ cxml::add_text_child(parent, "PassiveFTP", passive_ftp ? "1" : "0");
+ cxml::add_text_child(parent, "Host", host);
+ cxml::add_text_child(parent, "Path", path);
+ cxml::add_text_child(parent, "User", user);
+ cxml::add_text_child(parent, "Password", password);
+}
+
+
+bool
+operator==(UploadDestination const& a, UploadDestination const& b)
+{
+ return a.name == b.name
+ && a.protocol == b.protocol
+ && a.passive_ftp == b.passive_ftp
+ && a.host == b.host
+ && a.path == b.path
+ && a.user == b.user
+ && a.password == b.password;
+}
diff --git a/src/lib/upload_destination.h b/src/lib/upload_destination.h
new file mode 100644
index 000000000..8913c82f1
--- /dev/null
+++ b/src/lib/upload_destination.h
@@ -0,0 +1,69 @@
+/*
+ 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_UPLOAD_DESTINATION_H
+#define DCPOMATIC_UPLOAD_DESTINATION_H
+
+
+#include "types.h"
+#include <libcxml/cxml.h>
+
+
+class UploadDestination
+{
+public:
+ UploadDestination(
+ std::string name_,
+ FileTransferProtocol protocol_,
+ bool passive_ftp_,
+ std::string host_,
+ std::string path_,
+ std::string user_,
+ std::string password_
+ )
+ : name(name_)
+ , protocol(protocol_)
+ , passive_ftp(passive_ftp_)
+ , host(host_)
+ , path(path_)
+ , user(user_)
+ , password(password_)
+ {}
+
+ UploadDestination(cxml::ConstNodePtr node);
+
+ void as_xml(xmlpp::Element* parent) const;
+
+ std::string name;
+ FileTransferProtocol protocol;
+ bool passive_ftp = true;
+ std::string host;
+ std::string path;
+ std::string user;
+ std::string password;
+};
+
+
+bool operator==(UploadDestination const& a, UploadDestination const& b);
+
+
+#endif
+
diff --git a/src/lib/upload_job.cc b/src/lib/upload_job.cc
index 113e3a7e8..b43526c56 100644
--- a/src/lib/upload_job.cc
+++ b/src/lib/upload_job.cc
@@ -32,22 +32,23 @@
#include "log.h"
#include "scp_uploader.h"
#include "upload_job.h"
-#include <iostream>
+#include <fmt/format.h>
#include "i18n.h"
-using std::string;
using std::min;
using std::shared_ptr;
+using std::string;
using boost::scoped_ptr;
#if BOOST_VERSION >= 106100
using namespace boost::placeholders;
#endif
-UploadJob::UploadJob (shared_ptr<const Film> film)
+UploadJob::UploadJob(shared_ptr<const Film> film, UploadDestination destination)
: Job (film)
+ , _destination(destination)
, _status (_("Waiting"))
{
@@ -63,7 +64,7 @@ UploadJob::~UploadJob ()
string
UploadJob::name () const
{
- return _("Copy DCP to TMS");
+ return fmt::format(_("Copy DCP to {}"), _destination.name);
}
@@ -80,12 +81,12 @@ UploadJob::run ()
LOG_GENERAL_NC (N_("Upload job starting"));
scoped_ptr<Uploader> uploader;
- switch (Config::instance()->tms_protocol()) {
+ switch (_destination.protocol) {
case FileTransferProtocol::SCP:
- uploader.reset (new SCPUploader(bind (&UploadJob::set_status, this, _1), bind(&UploadJob::set_progress, this, _1, false)));
+ uploader.reset(new SCPUploader(_destination, bind(&UploadJob::set_status, this, _1), bind(&UploadJob::set_progress, this, _1, false)));
break;
case FileTransferProtocol::FTP:
- uploader.reset (new CurlUploader(bind (&UploadJob::set_status, this, _1), bind(&UploadJob::set_progress, this, _1, false)));
+ uploader.reset(new CurlUploader(_destination, bind(&UploadJob::set_status, this, _1), bind(&UploadJob::set_progress, this, _1, false)));
break;
}
diff --git a/src/lib/upload_job.h b/src/lib/upload_job.h
index a3da164bf..ae8d5c7a2 100644
--- a/src/lib/upload_job.h
+++ b/src/lib/upload_job.h
@@ -25,12 +25,13 @@
#include "job.h"
+#include "upload_destination.h"
class UploadJob : public Job
{
public:
- explicit UploadJob (std::shared_ptr<const Film>);
+ UploadJob(std::shared_ptr<const Film>, UploadDestination destination);
~UploadJob ();
std::string name () const override;
@@ -41,6 +42,8 @@ public:
private:
void set_status (std::string);
+ UploadDestination _destination;
+
mutable boost::mutex _status_mutex;
std::string _status;
};
diff --git a/src/lib/uploader.cc b/src/lib/uploader.cc
index f2a47f50e..507adad7b 100644
--- a/src/lib/uploader.cc
+++ b/src/lib/uploader.cc
@@ -31,8 +31,9 @@ using std::shared_ptr;
using std::function;
-Uploader::Uploader (function<void (string)> set_status, function<void (float)> set_progress)
+Uploader::Uploader(UploadDestination destination, function<void (string)> set_status, function<void (float)> set_progress)
: _set_progress (set_progress)
+ , _destination(std::move(destination))
, _set_status (set_status)
{
_set_status (_("connecting"));
diff --git a/src/lib/uploader.h b/src/lib/uploader.h
index 440746344..5fb1c129f 100644
--- a/src/lib/uploader.h
+++ b/src/lib/uploader.h
@@ -23,6 +23,7 @@
#define DCPOMATIC_UPLOADER_H
+#include "upload_destination.h"
#include <boost/filesystem.hpp>
@@ -32,7 +33,7 @@ class Job;
class Uploader
{
public:
- Uploader(std::function<void (std::string)> set_status, std::function<void (float)> set_progress);
+ Uploader(UploadDestination destination, std::function<void (std::string)> set_status, std::function<void (float)> set_progress);
virtual ~Uploader() {}
void upload(boost::filesystem::path directory);
@@ -43,6 +44,7 @@ protected:
virtual void upload_file(boost::filesystem::path from, boost::filesystem::path to, boost::uintmax_t& transferred, boost::uintmax_t total_size) = 0;
std::function<void (float)> _set_progress;
+ UploadDestination _destination;
private:
void upload_directory (boost::filesystem::path base, boost::filesystem::path directory, boost::uintmax_t& transferred, boost::uintmax_t total_size);
diff --git a/src/lib/wscript b/src/lib/wscript
index ea6994eb1..adb05f356 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -216,6 +216,7 @@ sources = """
stdout_log.cc
unzipper.cc
update_checker.cc
+ upload_destination.cc
upload_job.cc
uploader.cc
upmixer_a.cc
diff --git a/src/wx/full_config_dialog.cc b/src/wx/full_config_dialog.cc
index 704bce621..4e737caf9 100644
--- a/src/wx/full_config_dialog.cc
+++ b/src/wx/full_config_dialog.cc
@@ -53,6 +53,7 @@
#ifdef DCPOMATIC_GROK
#include "grok/gpu_config_panel.h"
#endif
+#include "upload_destination_dialog.h"
#include "wx_util.h"
#include "wx_variant.h"
#include "lib/config.h"
@@ -513,129 +514,58 @@ private:
};
-class TMSPage : public preferences::Page
+class UploadPage : public preferences::Page
{
public:
- TMSPage(wxSize panel_size, int border)
+ UploadPage(wxSize panel_size, int border)
: Page(panel_size, border)
{}
wxString GetName() const override
{
- return _("TMS");
+ return _("Upload");
}
#ifdef DCPOMATIC_OSX
wxBitmap GetLargeIcon() const override
{
- return wxBitmap(icon_path("tms"), wxBITMAP_TYPE_PNG);
+ return wxBitmap(icon_path("upload"), wxBITMAP_TYPE_PNG);
}
#endif
private:
void setup() override
{
- _upload = new CheckBox(_panel, _("Upload DCP to TMS after creation"));
- _panel->GetSizer()->Add(_upload, 0, wxALL | wxEXPAND, _border);
-
- wxFlexGridSizer* table = new wxFlexGridSizer(2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
- table->AddGrowableCol(1, 1);
- _panel->GetSizer()->Add(table, 1, wxALL | wxEXPAND, _border);
-
- add_label_to_sizer(table, _panel, _("Protocol"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
- _tms_protocol = new wxChoice(_panel, wxID_ANY);
- table->Add(_tms_protocol, 1, wxEXPAND);
-
- _tms_passive = new CheckBox(_panel, _("Passive mode"));
- table->Add(_tms_passive, 1, wxEXPAND);
- table->AddSpacer(0);
-
- add_label_to_sizer(table, _panel, _("IP address"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
- _tms_ip = new wxTextCtrl(_panel, wxID_ANY);
- table->Add(_tms_ip, 1, wxEXPAND);
-
- add_label_to_sizer(table, _panel, _("Target path"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
- _tms_path = new wxTextCtrl(_panel, wxID_ANY);
- table->Add(_tms_path, 1, wxEXPAND);
-
- add_label_to_sizer(table, _panel, _("User name"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
- _tms_user = new wxTextCtrl(_panel, wxID_ANY);
- table->Add(_tms_user, 1, wxEXPAND);
-
- add_label_to_sizer(table, _panel, _("Password"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
- _tms_password = new PasswordEntry(_panel);
- table->Add(_tms_password->get_panel(), 1, wxEXPAND);
-
- _tms_protocol->Append(_("SCP (for AAM and Doremi)"));
- _tms_protocol->Append(_("FTP (for Dolby)"));
-
- _upload->bind(&TMSPage::upload_changed, this);
- _tms_protocol->Bind(wxEVT_CHOICE, boost::bind(&TMSPage::tms_protocol_changed, this));
- _tms_passive->bind(&TMSPage::tms_passive_changed, this);
+ vector<EditableListColumn> columns;
+ columns.push_back(EditableListColumn(_("Name")));
+ columns.push_back(EditableListColumn(_("Host"), 256, true));
+ _destinations_list = new EditableList<UploadDestination, UploadDestinationDialog>(
+ _panel,
+ columns,
+ boost::bind(&Config::upload_destinations, Config::instance()),
+ boost::bind(&Config::set_upload_destinations, Config::instance(), _1),
+ [](UploadDestination const& destination, int column) -> std::string {
+ switch (column) {
+ case 0:
+ return destination.name;
+ case 1:
+ return destination.host;
+ }
+ return {};
+ },
+ EditableListTitle::VISIBLE,
+ EditableListButton::NEW | EditableListButton::EDIT | EditableListButton::REMOVE
+ );
- _tms_ip->Bind(wxEVT_TEXT, boost::bind(&TMSPage::tms_ip_changed, this));
- _tms_path->Bind(wxEVT_TEXT, boost::bind(&TMSPage::tms_path_changed, this));
- _tms_user->Bind(wxEVT_TEXT, boost::bind(&TMSPage::tms_user_changed, this));
- _tms_password->Changed.connect(boost::bind(&TMSPage::tms_password_changed, this));
+ _panel->GetSizer()->Add(_destinations_list, 1, wxEXPAND | wxALL, _border);
}
void config_changed() override
{
- auto config = Config::instance();
-
- checked_set(_upload, config->upload_after_make_dcp());
- checked_set(_tms_protocol, static_cast<int>(config->tms_protocol()));
- checked_set(_tms_passive, config->tms_protocol() == FileTransferProtocol::FTP && config->tms_passive());
- checked_set(_tms_ip, config->tms_ip());
- checked_set(_tms_path, config->tms_path());
- checked_set(_tms_user, config->tms_user());
- checked_set(_tms_password, config->tms_password());
-
- _tms_passive->Enable(config->tms_protocol() == FileTransferProtocol::FTP);
- }
-
- void upload_changed()
- {
- Config::instance()->set_upload_after_make_dcp(_upload->GetValue());
- }
-
- void tms_protocol_changed()
- {
- Config::instance()->set_tms_protocol(static_cast<FileTransferProtocol>(_tms_protocol->GetSelection()));
- }
-
- void tms_passive_changed()
- {
- Config::instance()->set_tms_passive(_tms_passive->get());
- }
- void tms_ip_changed()
- {
- Config::instance()->set_tms_ip(wx_to_std(_tms_ip->GetValue()));
- }
-
- void tms_path_changed()
- {
- Config::instance()->set_tms_path(wx_to_std(_tms_path->GetValue()));
- }
-
- void tms_user_changed()
- {
- Config::instance()->set_tms_user(wx_to_std(_tms_user->GetValue()));
- }
-
- void tms_password_changed()
- {
- Config::instance()->set_tms_password(_tms_password->get());
}
- CheckBox* _upload;
- CheckBox* _tms_passive;
- wxChoice* _tms_protocol;
- wxTextCtrl* _tms_ip;
- wxTextCtrl* _tms_path;
- wxTextCtrl* _tms_user;
- PasswordEntry* _tms_password;
+ EditableList<UploadDestination, UploadDestinationDialog>* _destinations_list;
};
@@ -1494,7 +1424,7 @@ create_full_config_dialog()
e->AddPage(new GPUPage(ps, border));
#endif
e->AddPage(new preferences::KeysPage(ps, border));
- e->AddPage(new TMSPage(ps, border));
+ e->AddPage(new UploadPage(ps, border));
e->AddPage(new preferences::EmailPage(ps, border));
e->AddPage(new preferences::KDMEmailPage(ps, border));
e->AddPage(new NotificationsPage(ps, border));
diff --git a/src/wx/upload_destination_dialog.cc b/src/wx/upload_destination_dialog.cc
new file mode 100644
index 000000000..55440b71a
--- /dev/null
+++ b/src/wx/upload_destination_dialog.cc
@@ -0,0 +1,105 @@
+/*
+ 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 "check_box.h"
+#include "dcpomatic_choice.h"
+#include "password_entry.h"
+#include "upload_destination_dialog.h"
+#include "wx_util.h"
+#include "lib/upload_destination.h"
+
+
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+
+UploadDestinationDialog::UploadDestinationDialog(wxWindow* parent)
+ : TableDialog(parent, _("Upload destination"), 2, 1, true)
+{
+ add(_("Name"), true);
+ _name = add(new wxTextCtrl(this, wxID_ANY, {}, wxDefaultPosition, wxSize(480, -1)));
+ add(_("Protocol"), true);
+ _protocol = add(new Choice(this));
+ _passive = add(new CheckBox(this, _("Passive")));
+ add_spacer();
+ add(_("Host"), true);
+ _host = add(new wxTextCtrl(this, wxID_ANY));
+ add(_("Path"), true);
+ _path = add(new wxTextCtrl(this, wxID_ANY));
+ add(_("User"), true);
+ _user = add(new wxTextCtrl(this, wxID_ANY));
+ add(_("Password"), true);
+ _password = new PasswordEntry(this);
+ add(_password->get_panel());
+
+ _protocol->add_entry(_("SCP"));
+ _protocol->add_entry(_("FTP"));
+
+ _protocol->bind(&UploadDestinationDialog::protocol_changed, this);
+
+ layout();
+
+ _name->SetFocus();
+
+ _protocol->set(0);
+ protocol_changed();
+}
+
+
+void
+UploadDestinationDialog::protocol_changed()
+{
+ auto const ftp = _protocol->get() == 1;
+ _passive->Enable(ftp);
+ _passive->SetValue(ftp);
+}
+
+
+void
+UploadDestinationDialog::set(UploadDestination const& destination)
+{
+ checked_set(_name, destination.name);
+ checked_set(_protocol, static_cast<int>(destination.protocol));
+ checked_set(_passive, destination.passive_ftp);
+ checked_set(_host, destination.host);
+ checked_set(_path, destination.path);
+ checked_set(_user, destination.user);
+ checked_set(_password, destination.password);
+
+ protocol_changed();
+}
+
+vector<UploadDestination>
+UploadDestinationDialog::get () const
+{
+ return {
+ UploadDestination(
+ wx_to_std(_name->GetValue()),
+ static_cast<FileTransferProtocol>(_protocol->GetSelection()),
+ _passive->get(),
+ wx_to_std(_host->GetValue()),
+ wx_to_std(_path->GetValue()),
+ wx_to_std(_user->GetValue()),
+ _password->get()
+ )
+ };
+}
diff --git a/src/wx/upload_destination_dialog.h b/src/wx/upload_destination_dialog.h
new file mode 100644
index 000000000..62abafe0f
--- /dev/null
+++ b/src/wx/upload_destination_dialog.h
@@ -0,0 +1,51 @@
+/*
+ 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 "table_dialog.h"
+#include "lib/upload_destination.h"
+#include <boost/optional.hpp>
+
+
+class CheckBox;
+class Choice;
+class PasswordEntry;
+class wxTextCtrl;
+
+
+class UploadDestinationDialog : public TableDialog
+{
+public:
+ explicit UploadDestinationDialog(wxWindow *);
+
+ void set(UploadDestination const& destination);
+ std::vector<UploadDestination> get() const;
+
+private:
+ void protocol_changed();
+
+ wxTextCtrl* _name;
+ Choice* _protocol;
+ CheckBox* _passive;
+ wxTextCtrl* _host;
+ wxTextCtrl* _path;
+ wxTextCtrl* _user;
+ PasswordEntry* _password;
+};
diff --git a/src/wx/wscript b/src/wx/wscript
index 3dfc157e2..3890a929c 100644
--- a/src/wx/wscript
+++ b/src/wx/wscript
@@ -185,6 +185,7 @@ sources = """
timeline_time_axis_view.cc
timing_panel.cc
try_unmount_dialog.cc
+ upload_destination_dialog.cc
update_dialog.cc
verify_dcp_dialog.cc
verify_dcp_progress_panel.cc