Use content version from Interop (or version number from SMPTE metadata)
[dcpomatic.git] / src / lib / film.cc
index a51ec0ad9ac58f35a3b49724fc588437ec44b794..5ccc5437a5cafedd6af5d32a950667772856d901 100644 (file)
@@ -170,7 +170,6 @@ Film::Film (optional<boost::filesystem::path> dir)
        , _user_explicit_resolution (false)
        , _name_language (dcp::LanguageTag("en-US"))
        , _audio_language (dcp::LanguageTag("en-US"))
-       , _release_territory (dcp::LanguageTag::RegionSubtag("US"))
        , _version_number (1)
        , _status (dcp::Status::FINAL)
        , _luminance (dcp::Luminance(4.5, dcp::Luminance::Unit::FOOT_LAMBERT))
@@ -477,19 +476,26 @@ Film::metadata (bool with_content_paths) const
        }
        root->add_child("NameLanguage")->add_child_text(_name_language.to_string());
        root->add_child("AudioLanguage")->add_child_text(_audio_language.to_string());
-       root->add_child("ReleaseTerritory")->add_child_text(_release_territory.subtag());
+       if (_release_territory) {
+               root->add_child("ReleaseTerritory")->add_child_text(_release_territory->subtag());
+       }
        root->add_child("VersionNumber")->add_child_text(raw_convert<string>(_version_number));
        root->add_child("Status")->add_child_text(dcp::status_to_string(_status));
-       root->add_child("Chain")->add_child_text(_chain);
-       root->add_child("Distributor")->add_child_text(_distributor);
-       root->add_child("Facility")->add_child_text(_facility);
-       root->add_child("LuminanceValue")->add_child_text(raw_convert<string>(_luminance.value()));
-       root->add_child("LuminanceUnit")->add_child_text(dcp::Luminance::unit_to_string(_luminance.unit()));
+       if (_chain) {
+               root->add_child("Chain")->add_child_text(*_chain);
+       }
+       if (_distributor) {
+               root->add_child("Distributor")->add_child_text(*_distributor);
+       }
+       if (_facility) {
+               root->add_child("Facility")->add_child_text(*_facility);
+       }
+       if (_luminance) {
+               root->add_child("LuminanceValue")->add_child_text(raw_convert<string>(_luminance->value()));
+               root->add_child("LuminanceUnit")->add_child_text(dcp::Luminance::unit_to_string(_luminance->unit()));
+       }
        root->add_child("UserExplicitContainer")->add_child_text(_user_explicit_container ? "1" : "0");
        root->add_child("UserExplicitResolution")->add_child_text(_user_explicit_resolution ? "1" : "0");
-       for (auto i: _subtitle_languages) {
-               root->add_child("SubtitleLanguage")->add_child_text(i.to_string());
-       }
        _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
@@ -657,24 +663,20 @@ Film::read_metadata (optional<boost::filesystem::path> path)
                _status = dcp::string_to_status (*status);
        }
 
-       _chain = f.optional_string_child("Chain").get_value_or("");
-       _distributor = f.optional_string_child("Distributor").get_value_or("");
-       _facility = f.optional_string_child("Facility").get_value_or("");
+       _chain = f.optional_string_child("Chain");
+       _distributor = f.optional_string_child("Distributor");
+       _facility = f.optional_string_child("Facility");
 
-       auto value = f.optional_number_child<float>("LuminanceValue").get_value_or(4.5);
+       auto value = f.optional_number_child<float>("LuminanceValue");
        auto unit = f.optional_string_child("LuminanceUnit");
-       if (unit) {
-               _luminance = dcp::Luminance (value, dcp::Luminance::string_to_unit(*unit));
+       if (value && unit) {
+               _luminance = dcp::Luminance (*value, dcp::Luminance::string_to_unit(*unit));
        }
 
        /* Disable guessing for files made in previous DCP-o-matic versions */
        _user_explicit_container = f.optional_bool_child("UserExplicitContainer").get_value_or(true);
        _user_explicit_resolution = f.optional_bool_child("UserExplicitResolution").get_value_or(true);
 
-       for (auto i: f.node_children("SubtitleLanguage")) {
-               _subtitle_languages.push_back (dcp::LanguageTag(i->content()));
-       }
-
        list<string> notes;
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
 
@@ -683,41 +685,6 @@ Film::read_metadata (optional<boost::filesystem::path> path)
                set_backtrace_file (file ("backtrace.txt"));
        }
 
-       /* Around 2.15.108 we removed subtitle language state from the text content and the ISDCF
-        * metadata and put it into the Film instead.  If we've loaded an old Film let's try and fish
-        * out the settings from where they were so that they don't get lost.
-        */
-
-       optional<dcp::LanguageTag> found_language;
-
-       for (auto i: f.node_child("Playlist")->node_children("Content")) {
-               auto text = i->optional_node_child("Text");
-               if (text && text->optional_string_child("Language") && !found_language) {
-                       try {
-                               found_language = dcp::LanguageTag(text->string_child("Language"));
-                       } catch (...) {}
-               }
-       }
-
-       if (_state_version >= 9) {
-               auto isdcf_language = f.node_child("ISDCFMetadata")->optional_string_child("SubtitleLanguage");
-               if (isdcf_language && !found_language) {
-                       try {
-                               found_language = dcp::LanguageTag(*isdcf_language);
-                       } catch (...) {
-                               try {
-                                       found_language = dcp::LanguageTag(boost::algorithm::to_lower_copy(*isdcf_language));
-                               } catch (...) {
-
-                               }
-                       }
-               }
-       }
-
-       if (found_language) {
-               _subtitle_languages.push_back (*found_language);
-       }
-
        _dirty = false;
        return notes;
 }
@@ -784,6 +751,50 @@ Film::mapped_audio_channels () const
        return mapped;
 }
 
+
+vector<dcp::LanguageTag>
+Film::audio_languages () const
+{
+       vector<dcp::LanguageTag> result;
+       for (auto i: content()) {
+               if (i->audio && !i->audio->mapping().mapped_output_channels().empty() && i->audio->language()) {
+                       result.push_back (i->audio->language().get());
+               }
+       }
+
+       std::sort (result.begin(), result.end());
+       auto last = std::unique (result.begin(), result.end());
+       result.erase (last, result.end());
+       return result;
+}
+
+
+pair<optional<dcp::LanguageTag>, vector<dcp::LanguageTag>>
+Film::subtitle_languages () const
+{
+       pair<optional<dcp::LanguageTag>, vector<dcp::LanguageTag>> result;
+       for (auto i: content()) {
+               for (auto j: i->text) {
+                       if (j->use() && j->type() == TextType::OPEN_SUBTITLE && j->language()) {
+                               if (j->language_is_additional()) {
+                                       result.second.push_back (j->language().get());
+                               } else {
+                                       result.first = j->language().get();
+                               }
+                       }
+               }
+               if (i->video && i->video->burnt_subtitle_language()) {
+                       result.first = i->video->burnt_subtitle_language();
+               }
+       }
+
+       std::sort (result.second.begin(), result.second.end());
+       auto last = std::unique (result.second.begin(), result.second.end());
+       result.second.erase (last, result.second.end());
+       return result;
+}
+
+
 /** @return a ISDCF-compliant name for a DCP of this film */
 string
 Film::isdcf_name (bool if_created_now) const
@@ -835,7 +846,18 @@ Film::isdcf_name (bool if_created_now) const
 
        if (dcp_content_type()) {
                d += "_" + dcp_content_type()->isdcf_name();
-               d += "-" + raw_convert<string>(isdcf_metadata().content_version);
+               string version = "1";
+               if (_interop) {
+                       if (!_content_versions.empty()) {
+                               auto cv = _content_versions[0];
+                               if (!cv.empty() && std::all_of(cv.begin(), cv.end(), isdigit)) {
+                                       version = cv;
+                               }
+                       }
+               } else {
+                       version = dcp::raw_convert<string>(_version_number);
+               }
+               d += "-" + version;
        }
 
        auto const dm = isdcf_metadata ();
@@ -895,41 +917,44 @@ Film::isdcf_name (bool if_created_now) const
                }
        }
 
-       if (!dm.audio_language.empty ()) {
-               d += "_" + dm.audio_language;
+       auto audio_langs = audio_languages();
+       auto audio_language = (audio_langs.empty() || !audio_langs.front().language()) ? "XX" : audio_langs.front().language()->subtag();
+       transform (audio_language.begin(), audio_language.end(), audio_language.begin(), ::toupper);
 
-               /* 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.
-               */
+       d += "_" + audio_language;
 
-               auto burnt_in = true;
-               auto ccap = false;
-               for (auto i: content()) {
-                       for (auto j: i->text) {
-                               if (j->type() == TextType::OPEN_SUBTITLE && j->use() && !j->burn()) {
-                                       burnt_in = false;
-                               } else if (j->type() == TextType::CLOSED_CAPTION && j->use()) {
-                                       ccap = true;
-                               }
-                       }
-               }
+       /* 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.
+       */
 
-               if (!_subtitle_languages.empty()) {
-                       auto lang = _subtitle_languages.front().language().get_value_or("en").subtag();
-                       if (burnt_in) {
-                               transform (lang.begin(), lang.end(), lang.begin(), ::tolower);
-                       } else {
-                               transform (lang.begin(), lang.end(), lang.begin(), ::toupper);
+       auto burnt_in = true;
+       auto ccap = false;
+       for (auto i: content()) {
+               for (auto j: i->text) {
+                       if (j->type() == TextType::OPEN_SUBTITLE && j->use() && !j->burn()) {
+                               burnt_in = false;
+                       } else if (j->type() == TextType::CLOSED_CAPTION && j->use()) {
+                               ccap = true;
                        }
+               }
+       }
 
-                       d += "-" + lang;
-                       if (ccap) {
-                               d += "-CCAP";
-                       }
+       auto sub_langs = subtitle_languages();
+       if (sub_langs.first && sub_langs.first->language()) {
+               auto lang = sub_langs.first->language()->subtag();
+               if (burnt_in) {
+                       transform (lang.begin(), lang.end(), lang.begin(), ::tolower);
                } else {
-                       /* No subtitles */
-                       d += "-XX";
+                       transform (lang.begin(), lang.end(), lang.begin(), ::toupper);
+               }
+
+               d += "-" + lang;
+               if (ccap) {
+                       d += "-CCAP";
                }
+       } else {
+               /* No subtitles */
+               d += "-XX";
        }
 
        if (!dm.territory.empty ()) {
@@ -1932,6 +1957,7 @@ Film::set_marker (dcp::Marker type, DCPTime time)
        _markers[type] = time;
 }
 
+
 void
 Film::unset_marker (dcp::Marker type)
 {
@@ -1980,7 +2006,7 @@ Film::set_audio_language (dcp::LanguageTag lang)
 
 
 void
-Film::set_release_territory (dcp::LanguageTag::RegionSubtag region)
+Film::set_release_territory (optional<dcp::LanguageTag::RegionSubtag> region)
 {
        FilmChangeSignaller ch (this, Property::RELEASE_TERRITORY);
        _release_territory = region;
@@ -2004,7 +2030,7 @@ Film::set_version_number (int v)
 
 
 void
-Film::set_chain (string c)
+Film::set_chain (optional<string> c)
 {
        FilmChangeSignaller ch (this, Property::CHAIN);
        _chain = c;
@@ -2012,7 +2038,7 @@ Film::set_chain (string c)
 
 
 void
-Film::set_distributor (string d)
+Film::set_distributor (optional<string> d)
 {
        FilmChangeSignaller ch (this, Property::DISTRIBUTOR);
        _distributor = d;
@@ -2020,7 +2046,7 @@ Film::set_distributor (string d)
 
 
 void
-Film::set_luminance (dcp::Luminance l)
+Film::set_luminance (optional<dcp::Luminance> l)
 {
        FilmChangeSignaller ch (this, Property::LUMINANCE);
        _luminance = l;
@@ -2028,30 +2054,7 @@ Film::set_luminance (dcp::Luminance l)
 
 
 void
-Film::set_subtitle_language (dcp::LanguageTag language)
-{
-       set_subtitle_languages ({language});
-}
-
-
-void
-Film::unset_subtitle_language ()
-{
-       FilmChangeSignaller ch (this, Property::SUBTITLE_LANGUAGES);
-       _subtitle_languages.clear();
-}
-
-
-void
-Film::set_subtitle_languages (vector<dcp::LanguageTag> languages)
-{
-       FilmChangeSignaller ch (this, Property::SUBTITLE_LANGUAGES);
-       _subtitle_languages = languages;
-}
-
-
-void
-Film::set_facility (string f)
+Film::set_facility (optional<string> f)
 {
        FilmChangeSignaller ch (this, Property::FACILITY);
        _facility = f;