Add config location versioning (#2090).
[dcpomatic.git] / src / tools / dcpomatic_disk.cc
index 5ae5cfb927168fb455d7cc8f173395835331c264..ec5d8e782db122dc4a15814432aa3ddae5bb2f2c 100644 (file)
 */
 
 
-#include "wx/wx_signal_manager.h"
-#include "wx/wx_util.h"
-#include "wx/job_manager_view.h"
+#include "wx/disk_warning_dialog.h"
 #include "wx/drive_wipe_warning_dialog.h"
-#include "wx/try_unmount_dialog.h"
+#include "wx/job_manager_view.h"
 #include "wx/message_dialog.h"
-#include "wx/disk_warning_dialog.h"
-#include "lib/file_log.h"
-#include "lib/dcpomatic_log.h"
-#include "lib/util.h"
+#include "wx/try_unmount_dialog.h"
+#include "wx/wx_util.h"
+#include "wx/wx_signal_manager.h"
+#include "wx/wx_util.h"
 #include "lib/config.h"
-#include "lib/signal_manager.h"
-#include "lib/cross.h"
 #include "lib/copy_to_drive_job.h"
-#include "lib/job_manager.h"
+#include "lib/cross.h"
+#include "lib/dcpomatic_log.h"
 #include "lib/disk_writer_messages.h"
+#include "lib/file_log.h"
+#include "lib/job_manager.h"
+#include "lib/signal_manager.h"
+#include "lib/util.h"
 #include "lib/version.h"
 #include "lib/warnings.h"
+#include <wx/cmdline.h>
 #include <wx/wx.h>
 DCPOMATIC_DISABLE_WARNINGS
 #include <boost/process.hpp>
@@ -49,11 +51,12 @@ DCPOMATIC_ENABLE_WARNINGS
 #endif
 
 
-using std::string;
-using std::exception;
-using std::cout;
 using std::cerr;
+using std::cout;
+using std::exception;
+using std::make_shared;
 using std::shared_ptr;
+using std::string;
 using boost::optional;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
@@ -136,7 +139,7 @@ public:
                /* XXX: this is a hack, but I expect we'll need logs and I'm not sure if there's
                 * a better place to put them.
                 */
-               dcpomatic_log.reset(new FileLog(config_path() / "disk.log"));
+               dcpomatic_log = make_shared<FileLog>(State::write_path("disk.log"));
                dcpomatic_log->set_types (dcpomatic_log->types() | LogEntry::TYPE_DISK);
                LOG_DISK("dcpomatic_disk %1 started", dcpomatic_git_commit);
 
@@ -171,6 +174,21 @@ public:
        ~DOMFrame ()
        {
                _nanomsg.send(DISK_WRITER_QUIT "\n", 2000);
+               /* This seems really horrible but it's suggested by the examples on nanomsg.org, so...
+                * Without this the quit is not received (at least sometimes) causing #2018.
+                */
+               dcpomatic_sleep_seconds (1);
+       }
+
+       void set_dcp (boost::filesystem::path dcp)
+       {
+               if (!boost::filesystem::exists(dcp / "ASSETMAP") && !boost::filesystem::exists(dcp / "ASSETMAP.xml")) {
+                       error_dialog (nullptr, _("No ASSETMAP or ASSETMAP.xml found in this folder.  Please choose a DCP folder."));
+                       return;
+               }
+
+               _dcp_path = dcp;
+               _dcp_name->SetLabel (std_to_wx(dcp.filename().string()));
        }
 
 private:
@@ -230,28 +248,49 @@ private:
                        return;
                }
 
-               _dcp_path = path;
-               _dcp_name->SetLabel (std_to_wx(_dcp_path->filename().string()));
+               set_dcp (path);
                setup_sensitivity ();
        }
 
        void copy ()
        {
+               /* Check that the selected drive still exists and update its properties if so */
+               drive_refresh ();
+               if (_drive->GetSelection() == wxNOT_FOUND) {
+                       error_dialog (this, _("The disk you selected is no longer available.  Please choose another."));
+                       return;
+               }
+
                DCPOMATIC_ASSERT (_drive->GetSelection() != wxNOT_FOUND);
                DCPOMATIC_ASSERT (static_cast<bool>(_dcp_path));
 
-               bool have_writer = true;
-               if (!_nanomsg.send(DISK_WRITER_PING "\n", 2000)) {
-                       have_writer = false;
-               } else {
-                       auto reply = _nanomsg.receive (2000);
-                       if (!reply || *reply != DISK_WRITER_PONG) {
-                               have_writer = false;
+               auto ping = [this](int attempt) {
+                       if (_nanomsg.send(DISK_WRITER_PING "\n", 1000)) {
+                               auto reply = _nanomsg.receive (1000);
+                               if (reply && *reply == DISK_WRITER_PONG) {
+                                       return true;
+                               } else if (reply) {
+                                       LOG_DISK("Unexpected response %1 to ping received (attempt %2)", *reply, attempt);
+                               } else {
+                                       LOG_DISK("No reply received from ping (attempt %1)", attempt);
+                               }
+                       } else {
+                               LOG_DISK("Could not send ping to writer (attempt %1)", attempt);
+                       }
+                       dcpomatic_sleep_seconds (1);
+                       return false;
+               };
+
+               bool have_writer = false;
+               for (int i = 0; i < 8; ++i) {
+                       if (ping(i + 1)) {
+                               have_writer = true;
+                               break;
                        }
                }
 
                if (!have_writer) {
-#ifdef DCPOMATIC_WINDOWS
+#if defined(DCPOMATIC_WINDOWS)
                        auto m = new MessageDialog (
                                this,
                                _("DCP-o-matic Disk Writer"),
@@ -260,7 +299,17 @@ private:
                        m->ShowModal ();
                        m->Destroy ();
                        return;
+#elif defined(DCPOMATIC_OSX)
+                       auto m = new MessageDialog (
+                               this,
+                               _("DCP-o-matic Disk Writer"),
+                               _("Did you install the DCP-o-matic Disk Writer.pkg from the .dmg?  Please check and try again.")
+                               );
+                       m->ShowModal ();
+                       m->Destroy ();
+                       return;
 #else
+                       LOG_DISK_NC ("Failed to ping writer");
                        throw CommunicationFailedError ();
 #endif
                }
@@ -276,12 +325,15 @@ private:
 
                        LOG_DISK("Sending unmount request to disk writer for %1", drive.as_xml());
                        if (!_nanomsg.send(DISK_WRITER_UNMOUNT "\n", 2000)) {
+                               LOG_DISK_NC("Failed to send unmount request.");
                                throw CommunicationFailedError ();
                        }
                        if (!_nanomsg.send(drive.as_xml(), 2000)) {
+                               LOG_DISK_NC("Failed to send drive for unmount request.");
                                throw CommunicationFailedError ();
                        }
-                       auto <string> reply = _nanomsg.receive (2000);
+                       /* The reply may have to wait for the user to authenticate, so let's wait a while */
+                       auto reply = _nanomsg.receive (30000);
                        if (!reply || *reply != DISK_WRITER_OK) {
                                auto * m = new MessageDialog (
                                                this,
@@ -352,11 +404,18 @@ private:
 };
 
 
+static const wxCmdLineEntryDesc command_line_description[] = {
+       { wxCMD_LINE_OPTION, "d", "dcp", "DCP to write", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
+       { wxCMD_LINE_SWITCH, "s", "sure", "skip alpha test warnings", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
+       { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
+};
+
+
 class App : public wxApp
 {
 public:
        App ()
-               : _frame (0)
+               : _frame (nullptr)
        {}
 
        bool OnInit ()
@@ -400,18 +459,24 @@ public:
                        */
                        Config::drop ();
 
-                       auto warning = new DiskWarningDialog ();
-                       warning->ShowModal ();
-                       if (!warning->confirmed()) {
-                               return false;
+                       if (!_skip_alpha_check) {
+                               auto warning = new DiskWarningDialog ();
+                               warning->ShowModal ();
+                               if (!warning->confirmed()) {
+                                       return false;
+                               }
+                               warning->Destroy ();
                        }
-                       warning->Destroy ();
 
                        _frame = new DOMFrame (_("DCP-o-matic Disk Writer"));
                        SetTopWindow (_frame);
 
                        _frame->Show ();
 
+                       if (_dcp_to_write) {
+                               _frame->set_dcp (*_dcp_to_write);
+                       }
+
                        signal_manager = new wxSignalManager (this);
                        Bind (wxEVT_IDLE, boost::bind (&App::idle, this, _1));
                }
@@ -424,6 +489,24 @@ public:
                return true;
        }
 
+       void OnInitCmdLine (wxCmdLineParser& parser)
+       {
+               parser.SetDesc (command_line_description);
+               parser.SetSwitchChars (wxT ("-"));
+       }
+
+       bool OnCmdLineParsed (wxCmdLineParser& parser)
+       {
+               _skip_alpha_check = parser.Found(wxT("sure"));
+
+               wxString dcp;
+               if (parser.Found(wxT("dcp"), &dcp)) {
+                       _dcp_to_write = wx_to_std (dcp);
+               }
+
+               return true;
+       }
+
        void config_failed_to_load ()
        {
                message_dialog (_frame, _("The existing configuration failed to load.  Default values will be used instead.  These may take a short time to create."));
@@ -479,6 +562,8 @@ public:
        }
 
        DOMFrame* _frame;
+       bool _skip_alpha_check = false;
+       boost::optional<boost::filesystem::path> _dcp_to_write;
 };
 
 IMPLEMENT_APP (App)