diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-10-22 21:06:41 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-10-22 21:06:41 +0100 |
| commit | 080da912e04d156d9260a3a5eead9034d2a72af3 (patch) | |
| tree | 2ebf01d3a283bd2780a3b5b25e6b3bcefb504c9e | |
| parent | 119178eddf7aa38083862a913e8519591b5b01a0 (diff) | |
Allow films to be loaded when content is missing.
| -rw-r--r-- | ChangeLog | 5 | ||||
| -rw-r--r-- | src/lib/content.cc | 24 | ||||
| -rw-r--r-- | src/lib/content.h | 5 | ||||
| -rw-r--r-- | src/lib/content_factory.cc | 17 | ||||
| -rw-r--r-- | src/lib/content_factory.h | 1 | ||||
| -rw-r--r-- | src/lib/film.cc | 6 | ||||
| -rw-r--r-- | src/lib/film.h | 2 | ||||
| -rw-r--r-- | src/lib/player.cc | 4 | ||||
| -rw-r--r-- | src/lib/playlist.cc | 12 | ||||
| -rw-r--r-- | src/lib/playlist.h | 2 | ||||
| -rw-r--r-- | src/wx/content_menu.cc | 107 | ||||
| -rw-r--r-- | src/wx/content_menu.h | 9 | ||||
| -rw-r--r-- | src/wx/film_editor.cc | 36 | ||||
| -rw-r--r-- | src/wx/film_viewer.cc | 4 |
14 files changed, 202 insertions, 32 deletions
@@ -1,3 +1,8 @@ +2013-10-22 Carl Hetherington <cth@carlh.net> + + * Allow films to be loaded when content is missing, + and then that content can be re-found. + 2013-10-21 Carl Hetherington <cth@carlh.net> * Version 1.19 released. diff --git a/src/lib/content.cc b/src/lib/content.cc index dbb841200..e3ad42560 100644 --- a/src/lib/content.cc +++ b/src/lib/content.cc @@ -31,10 +31,11 @@ using std::set; using boost::shared_ptr; using boost::lexical_cast; -int const ContentProperty::POSITION = 400; -int const ContentProperty::LENGTH = 401; -int const ContentProperty::TRIM_START = 402; -int const ContentProperty::TRIM_END = 403; +int const ContentProperty::PATH = 400; +int const ContentProperty::POSITION = 401; +int const ContentProperty::LENGTH = 402; +int const ContentProperty::TRIM_START = 403; +int const ContentProperty::TRIM_END = 404; Content::Content (shared_ptr<const Film> f, Time p) : _film (f) @@ -191,3 +192,18 @@ Content::identifier () const return s.str (); } + +bool +Content::path_valid () const +{ + return boost::filesystem::exists (_path); +} + +void +Content::set_path (boost::filesystem::path path) +{ + _path = path; + signal_changed (ContentProperty::PATH); +} + + diff --git a/src/lib/content.h b/src/lib/content.h index 9c7ad2fc2..c066c61e0 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -38,6 +38,7 @@ class Film; class ContentProperty { public: + static int const PATH; static int const POSITION; static int const LENGTH; static int const TRIM_START; @@ -61,12 +62,16 @@ public: virtual std::string identifier () const; boost::shared_ptr<Content> clone () const; + + void set_path (boost::filesystem::path); boost::filesystem::path path () const { boost::mutex::scoped_lock lm (_mutex); return _path; } + bool path_valid () const; + /** @return MD5 digest of the content's file(s) */ std::string digest () const { boost::mutex::scoped_lock lm (_mutex); diff --git a/src/lib/content_factory.cc b/src/lib/content_factory.cc index 6ed01f174..d42491f7f 100644 --- a/src/lib/content_factory.cc +++ b/src/lib/content_factory.cc @@ -22,6 +22,7 @@ #include "still_image_content.h" #include "moving_image_content.h" #include "sndfile_content.h" +#include "util.h" using std::string; using boost::shared_ptr; @@ -45,3 +46,19 @@ content_factory (shared_ptr<const Film> film, shared_ptr<cxml::Node> node) return content; } + +shared_ptr<Content> +content_factory (shared_ptr<const Film> film, boost::filesystem::path path) +{ + shared_ptr<Content> content; + + if (valid_image_file (path)) { + content.reset (new StillImageContent (film, path)); + } else if (SndfileContent::valid_file (path)) { + content.reset (new SndfileContent (film, path)); + } else { + content.reset (new FFmpegContent (film, path)); + } + + return content; +} diff --git a/src/lib/content_factory.h b/src/lib/content_factory.h index 27cd36024..93fd98d83 100644 --- a/src/lib/content_factory.h +++ b/src/lib/content_factory.h @@ -20,3 +20,4 @@ class Film; extern boost::shared_ptr<Content> content_factory (boost::shared_ptr<const Film>, boost::shared_ptr<cxml::Node>); +extern boost::shared_ptr<Content> content_factory (boost::shared_ptr<const Film>, boost::filesystem::path); diff --git a/src/lib/film.cc b/src/lib/film.cc index f869289d5..650163efe 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -844,6 +844,12 @@ Film::best_video_frame_rate () const return _playlist->best_dcp_frame_rate (); } +bool +Film::content_paths_valid () const +{ + return _playlist->content_paths_valid (); +} + void Film::playlist_content_changed (boost::weak_ptr<Content> c, int p) { diff --git a/src/lib/film.h b/src/lib/film.h index 8cfc49088..6bd04572b 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -111,10 +111,10 @@ public: /* Proxies for some Playlist methods */ ContentList content () const; - Time length () const; bool has_subtitles () const; OutputVideoFrame best_video_frame_rate () const; + bool content_paths_valid () const; libdcp::KDM make_kdm ( diff --git a/src/lib/player.cc b/src/lib/player.cc index f79265558..310e91b6c 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -480,6 +480,10 @@ Player::content_changed (weak_ptr<Content> w, int property, bool frequent) ) { Changed (frequent); + + } else if (property == ContentProperty::PATH) { + + Changed (frequent); } } diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc index 1712dc8ff..621b99dd7 100644 --- a/src/lib/playlist.cc +++ b/src/lib/playlist.cc @@ -390,3 +390,15 @@ Playlist::move_later (shared_ptr<Content> c) Changed (); } + +bool +Playlist::content_paths_valid () const +{ + for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) { + if (!(*i)->path_valid ()) { + return false; + } + } + + return true; +} diff --git a/src/lib/playlist.h b/src/lib/playlist.h index 05928ee57..a1ae9b151 100644 --- a/src/lib/playlist.h +++ b/src/lib/playlist.h @@ -80,6 +80,8 @@ public: void repeat (ContentList, int); + bool content_paths_valid () const; + mutable boost::signals2::signal<void ()> Changed; /** Third parameter is true if signals are currently being emitted frequently */ mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int, bool)> ContentChanged; diff --git a/src/wx/content_menu.cc b/src/wx/content_menu.cc index 60a95a9f2..6183e3444 100644 --- a/src/wx/content_menu.cc +++ b/src/wx/content_menu.cc @@ -18,16 +18,25 @@ */ #include <wx/wx.h> +#include <wx/dirdlg.h> #include "lib/playlist.h" #include "lib/film.h" +#include "lib/moving_image_content.h" +#include "lib/content_factory.h" +#include "lib/examine_content_job.h" +#include "lib/job_manager.h" #include "content_menu.h" #include "repeat_dialog.h" +#include "wx_util.h" using std::cout; using boost::shared_ptr; +using boost::weak_ptr; +using boost::dynamic_pointer_cast; enum { ID_repeat = 1, + ID_find_missing, ID_remove }; @@ -36,12 +45,14 @@ ContentMenu::ContentMenu (shared_ptr<Film> f, wxWindow* p) , _film (f) , _parent (p) { - _menu->Append (ID_repeat, _("Repeat...")); + _repeat = _menu->Append (ID_repeat, _("Repeat...")); + _find_missing = _menu->Append (ID_find_missing, _("Find missing...")); _menu->AppendSeparator (); - _menu->Append (ID_remove, _("Remove")); + _remove = _menu->Append (ID_remove, _("Remove")); - _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, &ContentMenu::repeat, this, ID_repeat); - _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, &ContentMenu::remove, this, ID_remove); + _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::repeat, this), ID_repeat); + _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::find_missing, this), ID_find_missing); + _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::remove, this), ID_remove); } ContentMenu::~ContentMenu () @@ -53,32 +64,38 @@ void ContentMenu::popup (ContentList c, wxPoint p) { _content = c; + _repeat->Enable (!_content.empty ()); + _find_missing->Enable (_content.size() == 1 && !_content.front()->path_valid ()); + _remove->Enable (!_content.empty ()); _parent->PopupMenu (_menu, p); } void -ContentMenu::repeat (wxCommandEvent &) +ContentMenu::repeat () { if (_content.empty ()) { return; } - RepeatDialog d (_parent); - d.ShowModal (); + RepeatDialog* d = new RepeatDialog (_parent); + if (d->ShowModal() != wxID_OK) { + d->Destroy (); + return; + } shared_ptr<const Film> film = _film.lock (); if (!film) { return; } - film->playlist()->repeat (_content, d.number ()); - d.Destroy (); + film->playlist()->repeat (_content, d->number ()); + d->Destroy (); _content.clear (); } void -ContentMenu::remove (wxCommandEvent &) +ContentMenu::remove () { if (_content.empty ()) { return; @@ -94,3 +111,73 @@ ContentMenu::remove (wxCommandEvent &) _content.clear (); } +void +ContentMenu::find_missing () +{ + if (_content.size() != 1) { + return; + } + + shared_ptr<const Film> film = _film.lock (); + if (!film) { + return; + } + + shared_ptr<Content> content; + + /* XXX: a bit nasty */ + if (dynamic_pointer_cast<MovingImageContent> (_content.front ())) { + wxDirDialog* d = new wxDirDialog (0, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST); + int const r = d->ShowModal (); + if (r == wxID_OK) { + content.reset (new MovingImageContent (film, boost::filesystem::path (wx_to_std (d->GetPath ())))); + } + d->Destroy (); + } else { + wxFileDialog* d = new wxFileDialog (0, _("Choose a file"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE); + int const r = d->ShowModal (); + if (r == wxID_OK) { + content = content_factory (film, wx_to_std (d->GetPath ())); + } + d->Destroy (); + } + + if (!content) { + return; + } + + shared_ptr<Job> j (new ExamineContentJob (film, content)); + + j->Finished.connect ( + bind ( + &ContentMenu::maybe_found_missing, + this, + boost::weak_ptr<Job> (j), + boost::weak_ptr<Content> (_content.front ()), + boost::weak_ptr<Content> (content) + ) + ); + + JobManager::instance()->add (j); +} + +void +ContentMenu::maybe_found_missing (weak_ptr<Job> j, weak_ptr<Content> oc, weak_ptr<Content> nc) +{ + shared_ptr<Job> job = j.lock (); + if (!job || !job->finished_ok ()) { + return; + } + + shared_ptr<Content> old_content = oc.lock (); + shared_ptr<Content> new_content = nc.lock (); + assert (old_content); + assert (new_content); + + if (new_content->digest() != old_content->digest()) { + error_dialog (0, _("The content file(s) you specified are not the same as those that are missing. Either try again with the correct content file or remove the missing content.")); + return; + } + + old_content->set_path (new_content->path ()); +} diff --git a/src/wx/content_menu.h b/src/wx/content_menu.h index 127fbea1a..2a6725320 100644 --- a/src/wx/content_menu.h +++ b/src/wx/content_menu.h @@ -36,13 +36,18 @@ public: void popup (ContentList, wxPoint); private: - void repeat (wxCommandEvent &); - void remove (wxCommandEvent &); + void repeat (); + void find_missing (); + void remove (); + void maybe_found_missing (boost::weak_ptr<Job>, boost::weak_ptr<Content>, boost::weak_ptr<Content>); wxMenu* _menu; boost::weak_ptr<Film> _film; wxWindow* _parent; ContentList _content; + wxMenuItem* _repeat; + wxMenuItem* _find_missing; + wxMenuItem* _remove; }; #endif diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index f2514da51..1472c8b89 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -44,6 +44,8 @@ #include "lib/sound_processor.h" #include "lib/scaler.h" #include "lib/playlist.h" +#include "lib/content.h" +#include "lib/content_factory.h" #include "timecode.h" #include "wx_util.h" #include "film_editor.h" @@ -490,6 +492,8 @@ FilmEditor::film_content_changed (weak_ptr<Content> weak_content, int property) if (property == FFmpegContentProperty::AUDIO_STREAM) { setup_dcp_name (); + } else if (property == ContentProperty::PATH) { + setup_content (); } } @@ -705,10 +709,22 @@ FilmEditor::setup_content () ContentList content = _film->content (); for (ContentList::iterator i = content.begin(); i != content.end(); ++i) { int const t = _content->GetItemCount (); - _content->InsertItem (t, std_to_wx ((*i)->summary ())); + bool const valid = (*i)->path_valid (); + + string s = (*i)->summary (); + if (!valid) { + s = _("MISSING: ") + s; + } + + _content->InsertItem (t, std_to_wx (s)); + if ((*i)->summary() == selected_summary) { _content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); } + + if (!valid) { + _content->SetItemTextColour (t, *wxRED); + } } if (selected_summary.empty () && !content.empty ()) { @@ -734,19 +750,7 @@ FilmEditor::content_add_file_clicked () /* XXX: check for lots of files here and do something */ for (unsigned int i = 0; i < paths.GetCount(); ++i) { - boost::filesystem::path p (wx_to_std (paths[i])); - - shared_ptr<Content> c; - - if (valid_image_file (p)) { - c.reset (new StillImageContent (_film, p)); - } else if (SndfileContent::valid_file (p)) { - c.reset (new SndfileContent (_film, p)); - } else { - c.reset (new FFmpegContent (_film, p)); - } - - _film->examine_and_add_content (c); + _film->examine_and_add_content (content_factory (_film, wx_to_std (paths[i]))); } } @@ -916,7 +920,9 @@ void FilmEditor::content_right_click (wxListEvent& ev) { ContentList cl; - cl.push_back (selected_content ()); + if (selected_content ()) { + cl.push_back (selected_content ()); + } _menu.popup (cl, ev.GetPoint ()); } diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 945644fb1..00aa6bef3 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -345,6 +345,10 @@ FilmViewer::fetch_next_frame () _play_button->SetValue (false); check_play_state (); error_dialog (this, wxString::Format (_("Could not decode video for view (%s)"), std_to_wx(e.what()).data())); + } catch (OpenFileError& e) { + /* There was a problem opening a content file; we'll let this slide as it + probably means a missing content file, which we're already taking care of. + */ } } |
