Add option to analyse audio automatically when content is added (#673).
[dcpomatic.git] / src / lib / job_manager.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /** @file  src/job_manager.cc
21  *  @brief A simple scheduler for jobs.
22  */
23
24 #include "job_manager.h"
25 #include "job.h"
26 #include "cross.h"
27 #include "analyse_audio_job.h"
28 #include "film.h"
29 #include <boost/thread.hpp>
30 #include <boost/foreach.hpp>
31 #include <iostream>
32
33 using std::string;
34 using std::list;
35 using std::cout;
36 using boost::shared_ptr;
37 using boost::weak_ptr;
38 using boost::function;
39 using boost::dynamic_pointer_cast;
40 using boost::optional;
41
42 JobManager* JobManager::_instance = 0;
43
44 JobManager::JobManager ()
45         : _terminate (false)
46         , _scheduler (0)
47 {
48
49 }
50
51 void
52 JobManager::start ()
53 {
54         _scheduler = new boost::thread (boost::bind (&JobManager::scheduler, this));
55 }
56
57 JobManager::~JobManager ()
58 {
59         {
60                 boost::mutex::scoped_lock lm (_mutex);
61                 _terminate = true;
62         }
63
64         if (_scheduler) {
65                 _scheduler->join ();
66         }
67
68         delete _scheduler;
69 }
70
71 shared_ptr<Job>
72 JobManager::add (shared_ptr<Job> j)
73 {
74         {
75                 boost::mutex::scoped_lock lm (_mutex);
76                 _jobs.push_back (j);
77         }
78
79         emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (j)));
80
81         return j;
82 }
83
84 list<shared_ptr<Job> >
85 JobManager::get () const
86 {
87         boost::mutex::scoped_lock lm (_mutex);
88         return _jobs;
89 }
90
91 bool
92 JobManager::work_to_do () const
93 {
94         boost::mutex::scoped_lock lm (_mutex);
95         list<shared_ptr<Job> >::const_iterator i = _jobs.begin();
96         while (i != _jobs.end() && (*i)->finished()) {
97                 ++i;
98         }
99
100         return i != _jobs.end ();
101 }
102
103 bool
104 JobManager::errors () const
105 {
106         boost::mutex::scoped_lock lm (_mutex);
107         for (list<shared_ptr<Job> >::const_iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
108                 if ((*i)->finished_in_error ()) {
109                         return true;
110                 }
111         }
112
113         return false;
114 }
115
116 void
117 JobManager::scheduler ()
118 {
119         while (true) {
120
121                 optional<string> active_job;
122
123                 {
124                         boost::mutex::scoped_lock lm (_mutex);
125                         if (_terminate) {
126                                 return;
127                         }
128
129                         BOOST_FOREACH (shared_ptr<Job> i, _jobs) {
130
131                                 if (!i->finished ()) {
132                                         active_job = i->json_name ();
133                                 }
134
135                                 if (i->running ()) {
136                                         /* Something is already happening */
137                                         break;
138                                 }
139
140                                 if (i->is_new()) {
141                                         i->start ();
142                                         /* Only start one job at once */
143                                         break;
144                                 }
145                         }
146                 }
147
148                 if (active_job != _last_active_job) {
149                         _last_active_job = active_job;
150                         emit (boost::bind (boost::ref (ActiveJobsChanged), active_job));
151                 }
152
153                 dcpomatic_sleep (1);
154         }
155 }
156
157 JobManager *
158 JobManager::instance ()
159 {
160         if (_instance == 0) {
161                 _instance = new JobManager ();
162                 _instance->start ();
163         }
164
165         return _instance;
166 }
167
168 void
169 JobManager::drop ()
170 {
171         delete _instance;
172         _instance = 0;
173 }
174
175 void
176 JobManager::analyse_audio (
177         shared_ptr<const Film> film,
178         shared_ptr<const Playlist> playlist,
179         boost::signals2::connection& connection,
180         function<void()> ready
181         )
182 {
183         shared_ptr<AnalyseAudioJob> job;
184
185         {
186                 boost::mutex::scoped_lock lm (_mutex);
187
188                 BOOST_FOREACH (shared_ptr<Job> i, _jobs) {
189                         shared_ptr<AnalyseAudioJob> a = dynamic_pointer_cast<AnalyseAudioJob> (i);
190                         if (a && film->audio_analysis_path (a->playlist ()) == film->audio_analysis_path (playlist)) {
191                                 i->when_finished (connection, ready);
192                                 return;
193                         }
194                 }
195
196                 job.reset (new AnalyseAudioJob (film, playlist));
197                 connection = job->Finished.connect (ready);
198                 _jobs.push_back (job);
199         }
200
201         emit (boost::bind (boost::ref (JobAdded), weak_ptr<Job> (job)));
202 }