Throw an exception if we try to make KDMs for an unencrypted project (#1188).
[dcpomatic.git] / src / lib / film.cc
index df19af77f062e6c5e18ac4c73872d1c0340d8d74..2b045f5c4c2fcef84503e4dc32874501deb94420 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -19,7 +19,7 @@
 */
 
 /** @file  src/film.cc
- *  @brief A representation of some audio and video content, and details of
+ *  @brief A representation of some audio, video and subtitle content, and details of
  *  how they should be presented in a DCP.
  */
 
@@ -27,6 +27,7 @@
 #include "job.h"
 #include "util.h"
 #include "job_manager.h"
+#include "dcp_encoder.h"
 #include "transcode_job.h"
 #include "upload_job.h"
 #include "null_log.h"
@@ -98,6 +99,8 @@ using dcp::raw_convert;
 #define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
 #define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
 
+string const Film::metadata_file = "metadata.xml";
+
 /* 5 -> 6
  * AudioMapping XML changed.
  * 6 -> 7
@@ -148,7 +151,7 @@ Film::Film (optional<boost::filesystem::path> dir)
        , _audio_processor (0)
        , _reel_type (REELTYPE_SINGLE)
        , _reel_length (2000000000)
-       , _upload_after_make_dcp (false)
+       , _upload_after_make_dcp (Config::instance()->default_upload_after_make_dcp())
        , _state_version (current_state_version)
        , _dirty (false)
 {
@@ -306,7 +309,7 @@ Film::make_dcp ()
        }
 
        if (name().empty()) {
-               throw MissingSettingError (_("name"));
+               set_name ("DCP");
        }
 
        BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
@@ -339,7 +342,9 @@ Film::make_dcp ()
        }
        LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
 
-       JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
+       shared_ptr<TranscodeJob> tj (new TranscodeJob (shared_from_this()));
+       tj->set_encoder (shared_ptr<Encoder> (new DCPEncoder (shared_from_this(), tj)));
+       JobManager::instance()->add (tj);
 }
 
 /** Start a job to send our DCP to the configured TMS */
@@ -399,7 +404,7 @@ Film::write_metadata () const
        DCPOMATIC_ASSERT (directory());
        boost::filesystem::create_directories (directory().get());
        shared_ptr<xmlpp::Document> doc = metadata ();
-       doc->write_to_file_formatted (file("metadata.xml").string ());
+       doc->write_to_file_formatted (file(metadata_file).string ());
        _dirty = false;
 }
 
@@ -419,11 +424,11 @@ list<string>
 Film::read_metadata (optional<boost::filesystem::path> path)
 {
        if (!path) {
-               if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
+               if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file (metadata_file))) {
                        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");
+               path = file (metadata_file);
        }
 
        cxml::Document f ("Metadata");
@@ -509,7 +514,7 @@ Film::read_metadata (optional<boost::filesystem::path> path)
 }
 
 /** Given a directory name, return its full path within the Film's directory.
- *  @param d directory name within the Filn's directory.
+ *  @param d directory name within the Film's directory.
  *  @param create true to create the directory (and its parents) if they do not exist.
  */
 boost::filesystem::path
@@ -664,7 +669,7 @@ Film::isdcf_name (bool if_created_now) const
 
        /* XXX: this uses the first bit of content only */
 
-       /* The standard says we don't do this for trailers, for some strange reason */
+       /* Interior aspect ratio.  The standard says we don't do this for trailers, for some strange reason */
        if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
                Ratio const * content_ratio = 0;
                BOOST_FOREACH (shared_ptr<Content> i, content ()) {
@@ -680,7 +685,8 @@ Film::isdcf_name (bool if_created_now) const
                }
 
                if (content_ratio && content_ratio != container()) {
-                       d += "-" + content_ratio->isdcf_name();
+                       /* This needs to be the numeric version of the ratio, and ::id() is close enough */
+                       d += "-" + content_ratio->id();
                }
        }
 
@@ -723,24 +729,9 @@ Film::isdcf_name (bool if_created_now) const
 
        /* Count mapped audio channels */
 
-       int non_lfe = 0;
-       int lfe = 0;
-
-       BOOST_FOREACH (int i, mapped_audio_channels ()) {
-               if (i >= audio_channels()) {
-                       /* This channel is mapped but is not included in the DCP */
-                       continue;
-               }
-
-               if (static_cast<dcp::Channel> (i) == dcp::LFE) {
-                       ++lfe;
-               } else {
-                       ++non_lfe;
-               }
-       }
-
-       if (non_lfe) {
-               d += String::compose("_%1%2", non_lfe, lfe);
+       pair<int, int> ch = audio_channel_types (mapped_audio_channels(), audio_channels());
+       if (ch.first) {
+               d += String::compose("_%1%2", ch.first, ch.second);
        }
 
        /* XXX: HI/VI */
@@ -1037,24 +1028,27 @@ Film::content () const
        return _playlist->content ();
 }
 
+/** @param content Content to add.
+ *  @param disable_audio_analysis true to never do automatic audio analysis, even if it is enabled in configuration.
+ */
 void
-Film::examine_and_add_content (shared_ptr<Content> c)
+Film::examine_and_add_content (shared_ptr<Content> content, bool disable_audio_analysis)
 {
-       if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
-               run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
+       if (dynamic_pointer_cast<FFmpegContent> (content) && _directory) {
+               run_ffprobe (content->path(0), file ("ffprobe.log"), _log);
        }
 
-       shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
+       shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), content));
 
        _job_connections.push_back (
-               j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job> (j), weak_ptr<Content> (c)))
+               j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job>(j), weak_ptr<Content>(content), disable_audio_analysis))
                );
 
        JobManager::instance()->add (j);
 }
 
 void
-Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
+Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c, bool disable_audio_analysis)
 {
        shared_ptr<Job> job = j.lock ();
        if (!job || !job->finished_ok ()) {
@@ -1068,7 +1062,7 @@ Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
 
        add_content (content);
 
-       if (Config::instance()->automatic_audio_analysis() && content->audio) {
+       if (Config::instance()->automatic_audio_analysis() && content->audio && !disable_audio_analysis) {
                shared_ptr<Playlist> playlist (new Playlist);
                playlist->add (content);
                boost::signals2::connection c;
@@ -1093,7 +1087,7 @@ Film::add_content (shared_ptr<Content> c)
                /* 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);
+                               c->take_settings_from (i);
                        }
                }
        }
@@ -1123,7 +1117,7 @@ Film::move_content_later (shared_ptr<Content> c)
 DCPTime
 Film::length () const
 {
-       return _playlist->length ();
+       return _playlist->length().ceil(video_frame_rate());
 }
 
 int
@@ -1224,6 +1218,10 @@ Film::make_kdm (
        dcp::Formulation formulation
        ) const
 {
+       if (!_encrypted) {
+               throw runtime_error (_("Cannot make a KDM as this project is not encrypted."));
+       }
+
        shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
        shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
        if (!signer->valid ()) {
@@ -1466,7 +1464,7 @@ list<DCPTimePeriod>
 Film::reels () const
 {
        list<DCPTimePeriod> p;
-       DCPTime const len = length().ceil (video_frame_rate ());
+       DCPTime const len = length();
 
        switch (reel_type ()) {
        case REELTYPE_SINGLE:
@@ -1526,42 +1524,6 @@ 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)
 {
@@ -1590,3 +1552,35 @@ Film::speed_up_range (int dcp_frame_rate) const
 {
        return _playlist->speed_up_range (dcp_frame_rate);
 }
+
+void
+Film::copy_from (shared_ptr<const Film> film)
+{
+       read_metadata (film->file (metadata_file));
+}
+
+bool
+Film::references_dcp_video () const
+{
+       BOOST_FOREACH (shared_ptr<Content> i, _playlist->content()) {
+               shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent>(i);
+               if (d && d->reference_video()) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+bool
+Film::references_dcp_audio () const
+{
+       BOOST_FOREACH (shared_ptr<Content> i, _playlist->content()) {
+               shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent>(i);
+               if (d && d->reference_audio()) {
+                       return true;
+               }
+       }
+
+       return false;
+}