Check for a DCP before offering to copy it to a TMS.
authorCarl Hetherington <cth@carlh.net>
Mon, 11 Feb 2013 23:56:33 +0000 (23:56 +0000)
committerCarl Hetherington <cth@carlh.net>
Mon, 11 Feb 2013 23:56:33 +0000 (23:56 +0000)
src/lib/film.cc
src/lib/film.h
src/tools/dvdomatic.cc
src/wx/film_editor.cc
src/wx/film_editor.h
test/metadata.ref
test/test.cc

index ff4d3b8f5a7fac14c8b24de5917dd5de78f62928..36ebe719975bdb11a80133c3edc57e0fe9890956 100644 (file)
@@ -63,7 +63,6 @@ using std::ofstream;
 using std::setfill;
 using std::min;
 using std::make_pair;
-using std::cout;
 using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::to_upper_copy;
@@ -104,6 +103,8 @@ Film::Film (string d, bool must_exist)
        , _frames_per_second (0)
        , _dirty (false)
 {
+       set_dci_date_today ();
+       
        /* Make state.directory a complete path without ..s (where possible)
           (Code swiped from Adam Bowen on stackoverflow)
        */
@@ -139,7 +140,6 @@ Film::Film (string d, bool must_exist)
        }
 
        _log = new FileLog (file ("log"));
-       set_dci_date_today ();
 }
 
 Film::Film (Film const & o)
@@ -171,6 +171,7 @@ Film::Film (Film const & o)
        , _colour_lut        (o._colour_lut)
        , _j2k_bandwidth     (o._j2k_bandwidth)
        , _dci_metadata      (o._dci_metadata)
+       , _dci_date          (o._dci_date)
        , _size              (o._size)
        , _length            (o._length)
        , _dcp_intrinsic_duration (o._dcp_intrinsic_duration)
@@ -409,6 +410,7 @@ Film::write_metadata () const
        f << "colour_lut " << _colour_lut << "\n";
        f << "j2k_bandwidth " << _j2k_bandwidth << "\n";
        _dci_metadata.write (f);
+       f << "dci_date " << boost::gregorian::to_iso_string (_dci_date) << "\n";
        f << "width " << _size.width << "\n";
        f << "height " << _size.height << "\n";
        f << "length " << _length.get_value_or(0) << "\n";
@@ -535,6 +537,8 @@ Film::read_metadata ()
                        _colour_lut = atoi (v.c_str ());
                } else if (k == "j2k_bandwidth") {
                        _j2k_bandwidth = atoi (v.c_str ());
+               } else if (k == "dci_date") {
+                       _dci_date = boost::gregorian::from_undelimited_string (v);
                }
 
                _dci_metadata.read (k, v);
@@ -696,7 +700,7 @@ Film::still_duration_in_frames () const
 
 /** @return a DCI-compliant name for a DCP of this film */
 string
-Film::dci_name () const
+Film::dci_name (bool if_created_now) const
 {
        stringstream d;
 
@@ -764,7 +768,11 @@ Film::dci_name () const
                d << dm.studio << "_";
        }
 
-       d << boost::gregorian::to_iso_string (_dci_date) << "_";
+       if (if_created_now) {
+               d << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ()) << "_";
+       } else {
+               d << boost::gregorian::to_iso_string (_dci_date) << "_";
+       }
 
        if (!dm.facility.empty ()) {
                d << dm.facility << "_";
@@ -779,10 +787,10 @@ Film::dci_name () const
 
 /** @return name to give the DCP */
 string
-Film::dcp_name () const
+Film::dcp_name (bool if_created_now) const
 {
        if (use_dci_name()) {
-               return dci_name ();
+               return dci_name (if_created_now);
        }
 
        return name();
@@ -1355,4 +1363,20 @@ Film::j2c_path (int f, bool t) const
        return file (p.string ());
 }
 
+/** Make an educated guess as to whether we have a complete DCP
+ *  or not.
+ *  @return true if we do.
+ */
 
+bool
+Film::have_dcp () const
+{
+       try {
+               libdcp::DCP dcp (dir (dcp_name()));
+               dcp.read ();
+       } catch (...) {
+               return false;
+       }
+
+       return true;
+}
index 5b65a099dc82d760f5f6e342bc6bb19719455bb1..cc77460db5f874dd5a9e07913b9936089352a5b8 100644 (file)
@@ -92,8 +92,8 @@ public:
        void read_metadata ();
 
        libdcp::Size cropped_size (libdcp::Size) const;
-       std::string dci_name () const;
-       std::string dcp_name () const;
+       std::string dci_name (bool if_created_now) const;
+       std::string dcp_name (bool if_created_now = false) const;
 
        boost::optional<int> dcp_intrinsic_duration () const {
                return _dcp_intrinsic_duration;
@@ -108,6 +108,8 @@ public:
 
        void set_dci_date_today ();
 
+       bool have_dcp () const;
+
        /** Identifiers for the parts of our state;
            used for signalling changes.
        */
@@ -373,9 +375,6 @@ private:
        /** Any running ExamineContentJob, or 0 */
        boost::shared_ptr<ExamineContentJob> _examine_content_job;
 
-       /** The date that we should use in a DCI name */
-       boost::gregorian::date _dci_date;
-
        void signal_changed (Property);
        void examine_content_finished ();
        std::string video_state_identifier () const;
@@ -452,6 +451,8 @@ private:
 
        /** DCI naming stuff */
        DCIMetadata _dci_metadata;
+       /** The date that we should use in a DCI name */
+       boost::gregorian::date _dci_date;
 
        /* Data which are cached to speed things up */
 
@@ -478,6 +479,7 @@ private:
        mutable boost::mutex _state_mutex;
 
        friend class paths_test;
+       friend class film_metadata_test;
 };
 
 #endif
index 4572678e3a6bc97c132dd29ae0e4b0aa2b836128..b90f3b07f22a654613fb97a6ef74b6c7ac24b8b3 100644 (file)
@@ -54,6 +54,7 @@ static FilmViewer* film_viewer = 0;
 static shared_ptr<Film> film;
 static std::string log_level;
 static std::string film_to_load;
+static wxMenu* jobs_menu = 0;
 
 static void set_menu_sensitivity ();
 
@@ -159,19 +160,19 @@ setup_menu (wxMenuBar* m)
        wxMenu* edit = new wxMenu;
        add_item (edit, "&Preferences...", ID_edit_preferences, ALWAYS);
 
-       wxMenu* jobs = new wxMenu;
-       add_item (jobs, "&Make DCP", ID_jobs_make_dcp, NEEDS_FILM);
-       add_item (jobs, "&Send DCP to TMS", ID_jobs_send_dcp_to_tms, NEEDS_FILM);
-       jobs->AppendSeparator ();
-       add_item (jobs, "&Examine content", ID_jobs_examine_content, NEEDS_FILM);
-       add_item (jobs, "Make DCP from existing &transcode", ID_jobs_make_dcp_from_existing_transcode, NEEDS_FILM);
+       jobs_menu = new wxMenu;
+       add_item (jobs_menu, "&Make DCP", ID_jobs_make_dcp, NEEDS_FILM);
+       add_item (jobs_menu, "&Send DCP to TMS", ID_jobs_send_dcp_to_tms, NEEDS_FILM);
+       jobs_menu->AppendSeparator ();
+       add_item (jobs_menu, "&Examine content", ID_jobs_examine_content, NEEDS_FILM);
+       add_item (jobs_menu, "Make DCP from existing &transcode", ID_jobs_make_dcp_from_existing_transcode, NEEDS_FILM);
 
        wxMenu* help = new wxMenu;
        add_item (help, "About", ID_help_about, ALWAYS);
 
        m->Append (file, _("&File"));
        m->Append (edit, _("&Edit"));
-       m->Append (jobs, _("&Jobs"));
+       m->Append (jobs_menu, _("&Jobs"));
        m->Append (help, _("&Help"));
 }
 
@@ -204,6 +205,8 @@ public:
                Connect (ID_jobs_make_dcp_from_existing_transcode, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_make_dcp_from_existing_transcode));
                Connect (ID_help_about, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::help_about));
 
+               Connect (wxID_ANY, wxEVT_MENU_OPEN, wxMenuEventHandler (Frame::menu_opened));
+
                wxPanel* panel = new wxPanel (this);
                wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
                s->Add (panel, 1, wxEXPAND);
@@ -237,6 +240,18 @@ public:
                set_film ();
        }
 
+private:
+
+       void menu_opened (wxMenuEvent& ev)
+       {
+               if (ev.GetMenu() != jobs_menu) {
+                       return;
+               }
+
+               bool const have_dcp = film && film->have_dcp();
+               jobs_menu->Enable (ID_jobs_send_dcp_to_tms, have_dcp);
+       }
+
        void set_film ()
        {
                film_viewer->set_film (film);
index 634e417df4ccf01c14c02f783f4b5eb2abfa4a27..9560842617433afccb9b4d4eaf4eb00ff7ed92b7 100644 (file)
@@ -585,7 +585,7 @@ FilmEditor::film_changed (Film::Property p)
                } else {
                        checked_set (_format, n);
                }
-               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_dcp_name ();
                break;
        }
        case Film::CROP:
@@ -608,8 +608,7 @@ FilmEditor::film_changed (Film::Property p)
        }
        case Film::NAME:
                checked_set (_name, _film->name());
-               _film->set_dci_date_today ();
-               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_dcp_name ();
                break;
        case Film::FRAMES_PER_SECOND:
                s << fixed << setprecision(2) << _film->frames_per_second();
@@ -639,7 +638,7 @@ FilmEditor::film_changed (Film::Property p)
                break;
        case Film::DCP_CONTENT_TYPE:
                checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ()));
-               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_dcp_name ();
                break;
        case Film::DCP_AB:
                checked_set (_dcp_ab, _film->dcp_ab ());
@@ -665,7 +664,7 @@ FilmEditor::film_changed (Film::Property p)
        case Film::WITH_SUBTITLES:
                checked_set (_with_subtitles, _film->with_subtitles ());
                setup_subtitle_control_sensitivity ();
-               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_dcp_name ();
                break;
        case Film::SUBTITLE_OFFSET:
                checked_set (_subtitle_offset, _film->subtitle_offset ());
@@ -681,23 +680,23 @@ FilmEditor::film_changed (Film::Property p)
                break;
        case Film::USE_DCI_NAME:
                checked_set (_use_dci_name, _film->use_dci_name ());
-               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_dcp_name ();
                break;
        case Film::DCI_METADATA:
-               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_dcp_name ();
                break;
        case Film::CONTENT_AUDIO_STREAM:
                if (_film->content_audio_stream()) {
                        checked_set (_audio_stream, _film->content_audio_stream()->to_string());
                }
-               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_dcp_name ();
                setup_audio_details ();
                setup_audio_control_sensitivity ();
                break;
        case Film::USE_CONTENT_AUDIO:
                checked_set (_use_content_audio, _film->use_content_audio());
                checked_set (_use_external_audio, !_film->use_content_audio());
-               _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+               setup_dcp_name ();
                setup_audio_details ();
                setup_audio_control_sensitivity ();
                break;
@@ -1171,3 +1170,9 @@ FilmEditor::external_audio_changed (wxCommandEvent &)
 
        _film->set_external_audio (a);
 }
+
+void
+FilmEditor::setup_dcp_name ()
+{
+       _dcp_name->SetLabel (std_to_wx (_film->dcp_name (true)));
+}
index 90be752d832a84d20611a9164016294bcab53607..8cb90b481dd7523578f57731ecbc044ff3d96a85 100644 (file)
@@ -94,6 +94,7 @@ private:
        void setup_audio_control_sensitivity ();
        void setup_streams ();
        void setup_audio_details ();
+       void setup_dcp_name ();
        
        wxControl* video_control (wxControl *);
        wxControl* still_control (wxControl *);
index 4250bdf23ff1780a4dfb1c0cde2b9e06b0158f02..ab40dfe8ddb873d80cd863670c188bb815f0b7ad 100644 (file)
@@ -31,6 +31,7 @@ rating
 studio 
 facility 
 package_type 
+dci_date 20130211
 width 0
 height 0
 length 0
index 771ef999288950e2ce60b297854c46ca1b7fc050..75199fac7ebd834e29d918405d1915a76b264a51 100644 (file)
@@ -21,6 +21,7 @@
 #include <iostream>
 #include <boost/filesystem.hpp>
 #include <boost/algorithm/string/predicate.hpp>
+#include <boost/date_time.hpp>
 #include "format.h"
 #include "film.h"
 #include "filter.h"
@@ -60,14 +61,25 @@ setup_test_config ()
        Config::instance()->set_default_dci_metadata (DCIMetadata ());
 }
 
+boost::filesystem::path
+test_film_dir (string name)
+{
+       boost::filesystem::path p;
+       p /= "build";
+       p /= "test";
+       p /= name;
+       return p;
+}
+
 shared_ptr<Film>
 new_test_film (string name)
 {
-       string const d = String::compose ("build/test/%1", name);
-       if (boost::filesystem::exists (d)) {
-               boost::filesystem::remove_all (d);
+       boost::filesystem::path p = test_film_dir (name);
+       if (boost::filesystem::exists (p)) {
+               boost::filesystem::remove_all (p);
        }
-       return shared_ptr<Film> (new Film (d, false));
+       
+       return shared_ptr<Film> (new Film (p.string(), false));
 }
 
 
@@ -144,6 +156,7 @@ BOOST_AUTO_TEST_CASE (film_metadata_test)
        BOOST_CHECK_THROW (new Film (test_film, true), OpenFileError);
        
        shared_ptr<Film> f (new Film (test_film, false));
+       f->_dci_date = boost::gregorian::from_undelimited_string ("20130211");
        BOOST_CHECK (f->format() == 0);
        BOOST_CHECK (f->dcp_content_type() == 0);
        BOOST_CHECK (f->filters ().empty());
@@ -468,6 +481,7 @@ BOOST_AUTO_TEST_CASE (make_dcp_test)
        film->set_format (Format::from_nickname ("Flat"));
        film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
        film->make_dcp (true);
+       film->write_metadata ();
 
        while (JobManager::instance()->work_to_do ()) {
                dvdomatic_sleep (1);
@@ -476,6 +490,19 @@ BOOST_AUTO_TEST_CASE (make_dcp_test)
        BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false);
 }
 
+/** Test Film::have_dcp().  Requires the output from make_dcp_test above */
+BOOST_AUTO_TEST_CASE (have_dcp_test)
+{
+       boost::filesystem::path p = test_film_dir ("make_dcp_test");
+       Film f (p.string ());
+       BOOST_CHECK (f.have_dcp());
+
+       p /= f.dcp_name();
+       p /= "video.mxf";
+       boost::filesystem::remove (p);
+       BOOST_CHECK (!f.have_dcp ());
+}
+
 BOOST_AUTO_TEST_CASE (make_dcp_with_range_test)
 {
        shared_ptr<Film> film = new_test_film ("make_dcp_with_range_test");