Tidy up handling of content-modified checks when using the dcpomatic_cli.
[dcpomatic.git] / src / lib / film.cc
index f61d5106f14e42c5a60e71d015ed1221d015c152..687033908db186257f93b2d84cf958796b10edf4 100644 (file)
@@ -99,6 +99,7 @@ using boost::dynamic_pointer_cast;
 using boost::optional;
 using boost::is_any_of;
 using dcp::raw_convert;
+using namespace dcpomatic;
 
 string const Film::metadata_file = "metadata.xml";
 
@@ -298,7 +299,7 @@ Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
 
 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
 void
-Film::make_dcp ()
+Film::make_dcp (bool gui)
 {
        if (dcp_name().find ("/") != string::npos) {
                throw BadSettingError (_("name"), _("Cannot contain slashes"));
@@ -352,7 +353,7 @@ Film::make_dcp ()
 
        shared_ptr<TranscodeJob> tj (new TranscodeJob (shared_from_this()));
        tj->set_encoder (shared_ptr<Encoder> (new DCPEncoder (shared_from_this(), tj)));
-       shared_ptr<CheckContentChangeJob> cc (new CheckContentChangeJob (shared_from_this(), tj));
+       shared_ptr<CheckContentChangeJob> cc (new CheckContentChangeJob(shared_from_this(), tj, gui));
        JobManager::instance()->add (cc);
 }
 
@@ -403,6 +404,14 @@ Film::metadata (bool with_content_paths) const
        root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
        root->add_child("ReencodeJ2K")->add_child_text (_reencode_j2k ? "1" : "0");
        root->add_child("UserExplicitVideoFrameRate")->add_child_text(_user_explicit_video_frame_rate ? "1" : "0");
+       for (map<dcp::Marker, DCPTime>::const_iterator i = _markers.begin(); i != _markers.end(); ++i) {
+               xmlpp::Element* m = root->add_child("Marker");
+               m->set_attribute("Type", dcp::marker_to_string(i->first));
+               m->add_child_text(raw_convert<string>(i->second.get()));
+       }
+       BOOST_FOREACH (dcp::Rating i, _ratings) {
+               i.as_xml (root->add_child("Rating"));
+       }
        _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
@@ -478,6 +487,7 @@ Film::read_metadata (optional<boost::filesystem::path> path)
                _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
        }
 
+
        {
                optional<string> c = f.optional_string_child ("DCPContentType");
                if (c) {
@@ -530,8 +540,15 @@ Film::read_metadata (optional<boost::filesystem::path> path)
        _reencode_j2k = f.optional_bool_child("ReencodeJ2K").get_value_or(false);
        _user_explicit_video_frame_rate = f.optional_bool_child("UserExplicitVideoFrameRate").get_value_or(false);
 
+       BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("Marker")) {
+               _markers[dcp::marker_from_string(i->string_attribute("Type"))] = DCPTime(dcp::raw_convert<DCPTime::Type>(i->content()));
+       }
+
+       BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("Rating")) {
+               _ratings.push_back (dcp::Rating(i));
+       }
+
        list<string> notes;
-       /* This method is the only one that can return notes (so far) */
        _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 */
@@ -722,36 +739,49 @@ Film::isdcf_name (bool if_created_now) const
 
        if (!dm.audio_language.empty ()) {
                d += "_" + dm.audio_language;
-               if (!dm.subtitle_language.empty()) {
 
-                       /* I'm not clear on the precise details of the convention for CCAP labelling;
-                          for now I'm just appending -CCAP if we have any closed captions.
-                       */
+               /* I'm not clear on the precise details of the convention for CCAP labelling;
+                  for now I'm just appending -CCAP if we have any closed captions.
+               */
 
-                       bool burnt_in = true;
-                       bool ccap = false;
-                       BOOST_FOREACH (shared_ptr<Content> i, content()) {
-                               BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
-                                       if (j->type() == TEXT_OPEN_SUBTITLE && j->use() && !j->burn()) {
+               optional<string> subtitle_language;
+               bool burnt_in = true;
+               bool ccap = false;
+               BOOST_FOREACH (shared_ptr<Content> i, content()) {
+                       BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
+                               if (j->type() == TEXT_OPEN_SUBTITLE && j->use()) {
+                                       subtitle_language = j->language ();
+                                       if (!j->burn()) {
                                                burnt_in = false;
-                                       } else if (j->type() == TEXT_CLOSED_CAPTION) {
-                                               ccap = true;
                                        }
+                               } else if (j->type() == TEXT_CLOSED_CAPTION && j->use()) {
+                                       ccap = true;
                                }
                        }
+               }
 
-                       string language = dm.subtitle_language;
-                       if (burnt_in && language != "XX") {
-                               transform (language.begin(), language.end(), language.begin(), ::tolower);
+               if (dm.subtitle_language) {
+                       /* Subtitle language is overridden in ISDCF metadata, primarily to handle
+                          content with pre-burnt subtitles.
+                       */
+                       d += "-" + *dm.subtitle_language;
+                       if (ccap) {
+                               d += "-CCAP";
+                       }
+               } else if (subtitle_language) {
+                       /* Language is worked out from the content */
+                       if (burnt_in && *subtitle_language != "XX") {
+                               transform (subtitle_language->begin(), subtitle_language->end(), subtitle_language->begin(), ::tolower);
                        } else {
-                               transform (language.begin(), language.end(), language.begin(), ::toupper);
+                               transform (subtitle_language->begin(), subtitle_language->end(), subtitle_language->begin(), ::toupper);
                        }
 
-                       d += "-" + language;
+                       d += "-" + *subtitle_language;
                        if (ccap) {
                                d += "-CCAP";
                        }
                } else {
+                       /* No subtitles */
                        d += "-XX";
                }
        }
@@ -1354,26 +1384,25 @@ Film::make_kdm (
 
        map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
 
-       BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
-               shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
-               if (!mxf || !mxf->key_id()) {
+       BOOST_FOREACH(shared_ptr<const dcp::ReelMXF> i, cpl->reel_mxfs()) {
+               if (!i->key_id()) {
                        continue;
                }
 
                /* Get any imported key for this ID */
                bool done = false;
                BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
-                       if (j.id() == mxf->key_id().get()) {
-                               LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
-                               keys[mxf] = j.key();
+                       if (j.id() == i->key_id().get()) {
+                               LOG_GENERAL ("Using imported key for %1", i->key_id().get());
+                               keys[i] = j.key();
                                done = true;
                        }
                }
 
                if (!done) {
                        /* No imported key; it must be an asset that we encrypted */
-                       LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
-                       keys[mxf] = key();
+                       LOG_GENERAL ("Using our own key for %1", i->key_id().get());
+                       keys[i] = key();
                }
        }
 
@@ -1391,7 +1420,7 @@ Film::make_kdm (
  *  @param disable_forensic_marking_audio if not set, don't disable forensic marking of audio.  If set to 0,
  *  disable all forensic marking; if set above 0, disable forensic marking above that channel.
  */
-list<ScreenKDM>
+list<shared_ptr<ScreenKDM> >
 Film::make_kdms (
        list<shared_ptr<Screen> > screens,
        boost::filesystem::path cpl_file,
@@ -1402,7 +1431,7 @@ Film::make_kdms (
        optional<int> disable_forensic_marking_audio
        ) const
 {
-       list<ScreenKDM> kdms;
+       list<shared_ptr<ScreenKDM> > kdms;
 
        BOOST_FOREACH (shared_ptr<Screen> i, screens) {
                if (i->recipient) {
@@ -1417,7 +1446,7 @@ Film::make_kdms (
                                disable_forensic_marking_audio
                                );
 
-                       kdms.push_back (ScreenKDM (i, kdm));
+                       kdms.push_back (shared_ptr<ScreenKDM>(new DCPScreenKDM(i, kdm)));
                }
        }
 
@@ -1662,7 +1691,7 @@ Film::closed_caption_tracks () const
        list<DCPTextTrack> tt;
        BOOST_FOREACH (shared_ptr<Content> i, content()) {
                BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
-                       /* XXX: Empty DCPTextTrack ends up being a magic value here */
+                       /* XXX: Empty DCPTextTrack ends up being a magic value here - the "unknown" or "not specified" track */
                        DCPTextTrack dtt = j->dcp_track().get_value_or(DCPTextTrack());
                        if (j->type() == TEXT_CLOSED_CAPTION && find(tt.begin(), tt.end(), dtt) == tt.end()) {
                                tt.push_back (dtt);
@@ -1672,3 +1701,34 @@ Film::closed_caption_tracks () const
 
        return tt;
 }
+
+void
+Film::set_marker (dcp::Marker type, DCPTime time)
+{
+       ChangeSignaller<Film> ch (this, MARKERS);
+       _markers[type] = time;
+}
+
+void
+Film::unset_marker (dcp::Marker type)
+{
+       ChangeSignaller<Film> ch (this, MARKERS);
+       _markers.erase (type);
+}
+
+void
+Film::set_ratings (vector<dcp::Rating> r)
+{
+       ChangeSignaller<Film> ch (this, RATINGS);
+       _ratings = r;
+}
+
+optional<DCPTime>
+Film::marker (dcp::Marker type) const
+{
+       map<dcp::Marker, DCPTime>::const_iterator i = _markers.find (type);
+       if (i == _markers.end()) {
+               return optional<DCPTime>();
+       }
+       return i->second;
+}