Use the same ContextID whenever encrypting the picture asset for a project;
[dcpomatic.git] / src / lib / film.cc
index a41ab3c95eb2c7722418ec814185a88c06e59701..d331516dd1e9cac9c7b9b979975a1d4578927266 100644 (file)
@@ -124,7 +124,7 @@ int const Film::current_state_version = 36;
  *  @param dir Film directory.
  */
 
-Film::Film (boost::filesystem::path dir, bool log)
+Film::Film (optional<boost::filesystem::path> dir)
        : _playlist (new Playlist)
        , _use_isdcf_name (true)
        , _dcp_content_type (Config::instance()->default_dcp_content_type ())
@@ -132,6 +132,7 @@ Film::Film (boost::filesystem::path dir, bool log)
        , _resolution (RESOLUTION_2K)
        , _signed (true)
        , _encrypted (false)
+       , _context_id (dcp::make_uuid ())
        , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
        , _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
        , _video_frame_rate (24)
@@ -152,27 +153,30 @@ Film::Film (boost::filesystem::path dir, bool log)
        _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
        _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2, _3));
 
-       /* Make state.directory a complete path without ..s (where possible)
-          (Code swiped from Adam Bowen on stackoverflow)
-          XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
-       */
+       if (dir) {
+               /* Make state.directory a complete path without ..s (where possible)
+                  (Code swiped from Adam Bowen on stackoverflow)
+                  XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
+               */
 
-       boost::filesystem::path p (boost::filesystem::system_complete (dir));
-       boost::filesystem::path result;
-       for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
-               if (*i == "..") {
-                       if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
+               boost::filesystem::path p (boost::filesystem::system_complete (dir.get()));
+               boost::filesystem::path result;
+               for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
+                       if (*i == "..") {
+                               if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
+                                       result /= *i;
+                               } else {
+                                       result = result.parent_path ();
+                               }
+                       } else if (*i != ".") {
                                result /= *i;
-                       } else {
-                               result = result.parent_path ();
                        }
-               } else if (*i != ".") {
-                       result /= *i;
                }
+
+               set_directory (result.make_preferred ());
        }
 
-       set_directory (result.make_preferred ());
-       if (log) {
+       if (_directory) {
                _log.reset (new FileLog (file ("log")));
        } else {
                _log.reset (new NullLog);
@@ -329,7 +333,7 @@ Film::send_dcp_to_tms ()
 }
 
 shared_ptr<xmlpp::Document>
-Film::metadata () const
+Film::metadata (bool with_content_paths) const
 {
        shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
        xmlpp::Element* root = doc->create_root_node ("Metadata");
@@ -358,13 +362,14 @@ Film::metadata () const
        root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
        root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
        root->add_child("Key")->add_child_text (_key.hex ());
+       root->add_child("ContextID")->add_child_text (_context_id);
        if (_audio_processor) {
                root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
        }
        root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
        root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
        root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
-       _playlist->as_xml (root->add_child ("Playlist"));
+       _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
 }
@@ -373,24 +378,38 @@ Film::metadata () const
 void
 Film::write_metadata () const
 {
-       boost::filesystem::create_directories (directory ());
+       DCPOMATIC_ASSERT (directory());
+       boost::filesystem::create_directories (directory().get());
        shared_ptr<xmlpp::Document> doc = metadata ();
        doc->write_to_file_formatted (file("metadata.xml").string ());
        _dirty = false;
 }
 
+/** Write a template from this film */
+void
+Film::write_template (boost::filesystem::path path) const
+{
+       boost::filesystem::create_directories (path.parent_path());
+       shared_ptr<xmlpp::Document> doc = metadata (false);
+       doc->write_to_file_formatted (path.string ());
+}
+
 /** Read state from our metadata file.
  *  @return Notes about things that the user should know about, or empty.
  */
 list<string>
-Film::read_metadata ()
+Film::read_metadata (optional<boost::filesystem::path> path)
 {
-       if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
-               throw runtime_error (_("This film was created with an older version of DCP-o-matic, and unfortunately it cannot be loaded into this version.  You will need to create a new Film, re-add your content and set it up again.  Sorry!"));
+       if (!path) {
+               if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
+                       throw runtime_error (_("This film was created with an older version of DCP-o-matic, and unfortunately it cannot be loaded into this version.  You will need to create a new Film, re-add your content and set it up again.  Sorry!"));
+               }
+
+               path = file ("metadata.xml");
        }
 
        cxml::Document f ("Metadata");
-       f.read_file (file ("metadata.xml"));
+       f.read_file (path.get ());
 
        _state_version = f.number_child<int> ("Version");
        if (_state_version > current_state_version) {
@@ -446,6 +465,7 @@ Film::read_metadata ()
        _three_d = f.bool_child ("ThreeD");
        _interop = f.bool_child ("Interop");
        _key = dcp::Key (f.string_child ("Key"));
+       _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ());
 
        if (f.optional_string_child ("AudioProcessor")) {
                _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
@@ -462,7 +482,9 @@ Film::read_metadata ()
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
 
        /* Write backtraces to this film's directory, until another film is loaded */
-       set_backtrace_file (file ("backtrace.txt"));
+       if (_directory) {
+               set_backtrace_file (file ("backtrace.txt"));
+       }
 
        _dirty = false;
        return notes;
@@ -474,8 +496,10 @@ Film::read_metadata ()
 boost::filesystem::path
 Film::dir (boost::filesystem::path d) const
 {
+       DCPOMATIC_ASSERT (_directory);
+
        boost::filesystem::path p;
-       p /= _directory;
+       p /= _directory.get();
        p /= d;
 
        boost::filesystem::create_directories (p);
@@ -489,8 +513,10 @@ Film::dir (boost::filesystem::path d) const
 boost::filesystem::path
 Film::file (boost::filesystem::path f) const
 {
+       DCPOMATIC_ASSERT (_directory);
+
        boost::filesystem::path p;
-       p /= _directory;
+       p /= _directory.get();
        p /= f;
 
        boost::filesystem::create_directories (p.parent_path ());
@@ -943,9 +969,13 @@ Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
 vector<CPLSummary>
 Film::cpls () const
 {
+       if (!directory ()) {
+               return vector<CPLSummary> ();
+       }
+
        vector<CPLSummary> out;
 
-       boost::filesystem::path const dir = directory ();
+       boost::filesystem::path const dir = directory().get();
        for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
                if (
                        boost::filesystem::is_directory (*i) &&
@@ -955,12 +985,13 @@ Film::cpls () const
                        try {
                                dcp::DCP dcp (*i);
                                dcp.read ();
+                               DCPOMATIC_ASSERT (dcp.cpls().front()->file());
                                out.push_back (
                                        CPLSummary (
                                                i->path().leaf().string(),
                                                dcp.cpls().front()->id(),
                                                dcp.cpls().front()->annotation_text(),
-                                               dcp.cpls().front()->file()
+                                               dcp.cpls().front()->file().get()
                                                )
                                        );
                        } catch (...) {
@@ -999,17 +1030,10 @@ Film::content () const
        return _playlist->content ();
 }
 
-void
-Film::examine_content (shared_ptr<Content> c)
-{
-       shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
-       JobManager::instance()->add (j);
-}
-
 void
 Film::examine_and_add_content (shared_ptr<Content> c)
 {
-       if (dynamic_pointer_cast<FFmpegContent> (c) && !_directory.empty ()) {
+       if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
                run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
        }
 
@@ -1036,6 +1060,7 @@ Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
        }
 
        add_content (content);
+
        if (Config::instance()->automatic_audio_analysis() && content->audio) {
                shared_ptr<Playlist> playlist (new Playlist);
                playlist->add (content);
@@ -1057,6 +1082,15 @@ Film::add_content (shared_ptr<Content> c)
                c->set_position (_playlist->subtitle_end ());
        }
 
+       if (_template_film) {
+               /* Take settings from the first piece of content of c's type in _template */
+               BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
+                       if (typeid(i.get()) == typeid(c.get())) {
+                               c->use_template (i);
+                       }
+               }
+       }
+
        _playlist->add (c);
 }
 
@@ -1352,24 +1386,12 @@ Film::audio_output_names () const
        DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
 
        vector<string> n;
-       n.push_back (_("L"));
-       n.push_back (_("R"));
-       n.push_back (_("C"));
-       n.push_back (_("Lfe"));
-       n.push_back (_("Ls"));
-       n.push_back (_("Rs"));
-       n.push_back (_("HI"));
-       n.push_back (_("VI"));
-       n.push_back (_("Lc"));
-       n.push_back (_("Rc"));
-       n.push_back (_("BsL"));
-       n.push_back (_("BsR"));
-       n.push_back (_("DBP"));
-       n.push_back (_("DBS"));
-       n.push_back ("");
-       n.push_back ("");
-
-       return vector<string> (n.begin(), n.begin() + audio_channels ());
+
+       for (int i = 0; i < audio_channels(); ++i) {
+               n.push_back (short_audio_channel_name (i));
+       }
+
+       return n;
 }
 
 void
@@ -1450,3 +1472,62 @@ Film::content_summary (DCPTimePeriod period) const
 {
        return _playlist->content_summary (period);
 }
+
+list<string>
+Film::fix_conflicting_settings ()
+{
+       list<string> notes;
+
+       list<boost::filesystem::path> was_referencing;
+       BOOST_FOREACH (shared_ptr<Content> i, content()) {
+               shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
+               if (d) {
+                       list<string> reasons;
+                       bool was = false;
+                       if (!d->can_reference_video(reasons) && d->reference_video()) {
+                               d->set_reference_video (false);
+                               was = true;
+                       }
+                       if (!d->can_reference_audio(reasons) && d->reference_audio()) {
+                               d->set_reference_audio (false);
+                               was = true;
+                       }
+                       if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
+                               d->set_reference_subtitle (false);
+                               was = true;
+                       }
+                       if (was) {
+                               was_referencing.push_back (d->path(0).parent_path().filename());
+                       }
+               }
+       }
+
+       BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
+               notes.push_back (String::compose (_("The DCP %1 was being referred to by this film.  This not now possible because the reel sizes in the film no longer agree with those in the imported DCP.\n\nSetting the 'Reel type' to 'split by video content' will probably help.\n\nAfter doing that you would need to re-tick the appropriate 'refer to existing DCP' checkboxes."), d.string()));
+       }
+
+       return notes;
+}
+
+void
+Film::use_template (string name)
+{
+       _template_film.reset (new Film (optional<boost::filesystem::path>()));
+       _template_film->read_metadata (Config::instance()->template_path (name));
+       _use_isdcf_name = _template_film->_use_isdcf_name;
+       _dcp_content_type = _template_film->_dcp_content_type;
+       _container = _template_film->_container;
+       _resolution = _template_film->_resolution;
+       _j2k_bandwidth = _template_film->_j2k_bandwidth;
+       _video_frame_rate = _template_film->_video_frame_rate;
+       _signed = _template_film->_signed;
+       _encrypted = _template_film->_encrypted;
+       _audio_channels = _template_film->_audio_channels;
+       _sequence = _template_film->_sequence;
+       _three_d = _template_film->_three_d;
+       _interop = _template_film->_interop;
+       _audio_processor = _template_film->_audio_processor;
+       _reel_type = _template_film->_reel_type;
+       _reel_length = _template_film->_reel_length;
+       _upload_after_make_dcp = _template_film->_upload_after_make_dcp;
+}