Try to not start jobs if a dependant fails.
[dcpomatic.git] / src / lib / film.cc
index 502119db3dee9ee714794108cccd33e24bcd64de..00d37c097aec72e676f2cfc485bdf57c3960900d 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <stdexcept>
 #include <iostream>
+#include <algorithm>
 #include <fstream>
 #include <cstdlib>
 #include <sstream>
@@ -26,9 +27,6 @@
 #include <unistd.h>
 #include <boost/filesystem.hpp>
 #include <boost/algorithm/string.hpp>
-#ifdef DVDOMATIC_WINDOWS
-#include <winsock2.h>
-#endif
 #include "film.h"
 #include "format.h"
 #include "tiff_encoder.h"
@@ -50,6 +48,8 @@
 #include "scaler.h"
 #include "decoder_factory.h"
 #include "config.h"
+#include "check_hashes_job.h"
+#include "version.h"
 
 using namespace std;
 using namespace boost;
@@ -71,7 +71,7 @@ Film::Film (string d, bool must_exist)
        
        filesystem::path p (filesystem::system_complete (d));
        filesystem::path result;
-       for(filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
+       for (filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
                if (*i == "..") {
                        if (filesystem::is_symlink (result) || result.filename() == "..") {
                                result /= *i;
@@ -85,13 +85,17 @@ Film::Film (string d, bool must_exist)
 
        _state.directory = result.string ();
        
-       if (must_exist && !filesystem::exists (_state.directory)) {
-               throw OpenFileError (_state.directory);
+       if (!filesystem::exists (_state.directory)) {
+               if (must_exist) {
+                       throw OpenFileError (_state.directory);
+               } else {
+                       filesystem::create_directory (_state.directory);
+               }
        }
 
        read_metadata ();
 
-       _log = new Log (_state.file ("log"));
+       _log = new FileLog (_state.file ("log"));
 }
 
 /** Copy constructor */
@@ -125,6 +129,10 @@ Film::read_metadata ()
                        continue;
                }
 
+               if (line[line.size() - 1] == '\r') {
+                       line = line.substr (0, line.size() - 1);
+               }
+
                size_t const s = line.find (' ');
                if (s == string::npos) {
                        continue;
@@ -168,7 +176,24 @@ Film::set_name (string n)
 void
 Film::set_content (string c)
 {
-       if (filesystem::path(c).has_root_directory () && starts_with (c, _state.directory)) {
+       string check = _state.directory;
+
+#if BOOST_FILESYSTEM_VERSION == 3
+       filesystem::path slash ("/");
+       string platform_slash = slash.make_preferred().string ();
+#else
+#ifdef DVDOMATIC_WINDOWS
+       string platform_slash = "\\";
+#else
+       string platform_slash = "/";
+#endif
+#endif 
+
+       if (!ends_with (check, platform_slash)) {
+               check += platform_slash;
+       }
+       
+       if (filesystem::path(c).has_root_directory () && starts_with (c, check)) {
                c = c.substr (_state.directory.length() + 1);
        }
 
@@ -193,6 +218,7 @@ Film::set_content (string c)
        _state.audio_sample_rate = d->audio_sample_rate ();
        _state.audio_sample_format = d->audio_sample_format ();
 
+       _state.content_digest = md5_digest (s->content_path ());
        _state.content = c;
        
        signal_changed (SIZE);
@@ -225,48 +251,48 @@ Film::set_dcp_content_type (DCPContentType const * t)
 void
 Film::set_left_crop (int c)
 {
-       if (c == _state.left_crop) {
+       if (c == _state.crop.left) {
                return;
        }
        
-       _state.left_crop = c;
-       signal_changed (LEFT_CROP);
+       _state.crop.left = c;
+       signal_changed (CROP);
 }
 
 /** Set the number of pixels by which to crop the right of the source video */
 void
 Film::set_right_crop (int c)
 {
-       if (c == _state.right_crop) {
+       if (c == _state.crop.right) {
                return;
        }
 
-       _state.right_crop = c;
-       signal_changed (RIGHT_CROP);
+       _state.crop.right = c;
+       signal_changed (CROP);
 }
 
 /** Set the number of pixels by which to crop the top of the source video */
 void
 Film::set_top_crop (int c)
 {
-       if (c == _state.top_crop) {
+       if (c == _state.crop.top) {
                return;
        }
        
-       _state.top_crop = c;
-       signal_changed (TOP_CROP);
+       _state.crop.top = c;
+       signal_changed (CROP);
 }
 
 /** Set the number of pixels by which to crop the bottom of the source video */
 void
 Film::set_bottom_crop (int c)
 {
-       if (c == _state.bottom_crop) {
+       if (c == _state.crop.bottom) {
                return;
        }
        
-       _state.bottom_crop = c;
-       signal_changed (BOTTOM_CROP);
+       _state.crop.bottom = c;
+       signal_changed (CROP);
 }
 
 /** Set the filters to apply to the image when generating thumbnails
@@ -412,10 +438,10 @@ Film::j2k_dir () const
 {
        assert (format());
 
-       stringstream s;
+       filesystem::path p;
 
        /* Start with j2c */
-       s << "j2c/";
+       p /= "j2c";
 
        pair<string, string> f = Filter::ffmpeg_strings (filters ());
 
@@ -423,19 +449,24 @@ Film::j2k_dir () const
           so that we don't get confused about J2K files generated using different
           settings.
        */
-       s << _state.format->nickname()
-         << "_" << _state.content
-         << "_" << left_crop() << "_" << right_crop() << "_" << top_crop() << "_" << bottom_crop()
+       stringstream s;
+       s << _state.format->id()
+         << "_" << _state.content_digest
+         << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
          << "_" << f.first << "_" << f.second
          << "_" << _state.scaler->id();
 
+       p /= s.str ();
+
        /* Similarly for the A/B case */
        if (dcp_ab()) {
+               stringstream s;
                pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
-               s << "/ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
+               s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
+               p /= s.str ();
        }
        
-       return _state.dir (s.str ());
+       return _state.dir (p.string ());
 }
 
 /** Handle a change to the Film's metadata */
@@ -457,18 +488,12 @@ Film::make_dcp (bool transcode, int freq)
                throw BadSettingError ("name", "cannot contain slashes");
        }
        
-       {
-               stringstream s;
-               s << "DVD-o-matic " << DVDOMATIC_VERSION << " using " << dependency_version_summary ();
-               log()->log (s.str ());
-       }
+       log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary()));
 
        {
                char buffer[128];
                gethostname (buffer, sizeof (buffer));
-               stringstream s;
-               s << "Starting to make a DCP on " << buffer;
-               log()->log (s.str ());
+               log()->log (String::compose ("Starting to make DCP on %1", buffer));
        }
                
        if (format() == 0) {
@@ -492,35 +517,35 @@ Film::make_dcp (bool transcode, int freq)
        o->out_size = format()->dcp_size ();
        if (dcp_frames() == 0) {
                /* Decode the whole film, no blacking */
-               o->num_frames = 0;
                o->black_after = 0;
        } else {
                switch (dcp_trim_action()) {
                case CUT:
                        /* Decode only part of the film, no blacking */
-                       o->num_frames = dcp_frames ();
                        o->black_after = 0;
                        break;
                case BLACK_OUT:
                        /* Decode the whole film, but black some frames out */
-                       o->num_frames = 0;
                        o->black_after = dcp_frames ();
                }
        }
        
        o->decode_video_frequency = freq;
-       o->padding = format()->dcp_padding ();
-       o->ratio = format()->ratio_as_float ();
+       o->padding = format()->dcp_padding (this);
+       o->ratio = format()->ratio_as_float (this);
+
+       shared_ptr<Job> r;
 
        if (transcode) {
                if (_state.dcp_ab) {
-                       JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (fs, o, log ())));
+                       r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (fs, o, log(), shared_ptr<Job> ())));
                } else {
-                       JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (fs, o, log ())));
+                       r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (fs, o, log(), shared_ptr<Job> ())));
                }
        }
-       
-       JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (fs, o, log ())));
+
+       r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (fs, o, log(), r)));
+       JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (fs, o, log(), r)));
 }
 
 shared_ptr<FilmState>
@@ -559,7 +584,7 @@ Film::examine_content ()
                return;
        }
        
-       _examine_content_job.reset (new ExamineContentJob (state_copy (), log ()));
+       _examine_content_job.reset (new ExamineContentJob (state_copy (), log(), shared_ptr<Job> ()));
        _examine_content_job->Finished.connect (sigc::mem_fun (*this, &Film::examine_content_post_gui));
        JobManager::instance()->add (_examine_content_job);
 }
@@ -580,13 +605,6 @@ Film::set_scaler (Scaler const * s)
        signal_changed (SCALER);
 }
 
-void
-Film::set_frames_per_second (float f)
-{
-       _state.frames_per_second = f;
-       signal_changed (FRAMES_PER_SECOND);
-}
-
 /** @return full paths to any audio files that this Film has */
 vector<string>
 Film::audio_files () const
@@ -615,15 +633,30 @@ Film::set_still_duration (int d)
 void
 Film::send_dcp_to_tms ()
 {
-       shared_ptr<Job> j (new SCPDCPJob (state_copy (), log ()));
+       shared_ptr<Job> j (new SCPDCPJob (state_copy (), log(), shared_ptr<Job> ()));
        JobManager::instance()->add (j);
 }
 
 void
 Film::copy_from_dvd ()
 {
-       shared_ptr<Job> j (new CopyFromDVDJob (state_copy (), log ()));
+       shared_ptr<Job> j (new CopyFromDVDJob (state_copy (), log(), shared_ptr<Job> ()));
        j->Finished.connect (sigc::mem_fun (*this, &Film::copy_from_dvd_post_gui));
        JobManager::instance()->add (j);
 }
 
+int
+Film::encoded_frames () const
+{
+       if (format() == 0) {
+               return 0;
+       }
+
+       int N = 0;
+       for (filesystem::directory_iterator i = filesystem::directory_iterator (j2k_dir ()); i != filesystem::directory_iterator(); ++i) {
+               ++N;
+               this_thread::interruption_point ();
+       }
+
+       return N;
+}