diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-04-29 00:47:47 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-06-03 22:46:26 +0200 |
| commit | fb5b8ae43e373c37b78202217528be73efa162cc (patch) | |
| tree | 9dfb23d6188ba86485795eb13876ebc01f560874 /src | |
| parent | eb2452d1d76e7b87a61e88773b29206ff5833e34 (diff) | |
Allow multiple upload destinations instead of just the TMS.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/config.cc | 73 | ||||
| -rw-r--r-- | src/lib/config.h | 77 | ||||
| -rw-r--r-- | src/lib/curl_uploader.cc | 12 | ||||
| -rw-r--r-- | src/lib/curl_uploader.h | 2 | ||||
| -rw-r--r-- | src/lib/dcp_transcode_job.cc | 13 | ||||
| -rw-r--r-- | src/lib/film.cc | 3 | ||||
| -rw-r--r-- | src/lib/scp_uploader.cc | 14 | ||||
| -rw-r--r-- | src/lib/scp_uploader.h | 2 | ||||
| -rw-r--r-- | src/lib/upload_destination.cc | 62 | ||||
| -rw-r--r-- | src/lib/upload_destination.h | 69 | ||||
| -rw-r--r-- | src/lib/upload_job.cc | 15 | ||||
| -rw-r--r-- | src/lib/upload_job.h | 5 | ||||
| -rw-r--r-- | src/lib/uploader.cc | 3 | ||||
| -rw-r--r-- | src/lib/uploader.h | 4 | ||||
| -rw-r--r-- | src/lib/wscript | 1 | ||||
| -rw-r--r-- | src/wx/full_config_dialog.cc | 126 | ||||
| -rw-r--r-- | src/wx/upload_destination_dialog.cc | 105 | ||||
| -rw-r--r-- | src/wx/upload_destination_dialog.h | 51 | ||||
| -rw-r--r-- | src/wx/wscript | 1 |
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 |
