diff options
| author | Carl Hetherington <cth@carlh.net> | 2024-12-14 18:53:00 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-02-02 15:46:34 +0100 |
| commit | ccfea0444597a54638a991a413937f503039fd62 (patch) | |
| tree | c0373a0cb09e1f4cf69be3be75c380a3937e408c | |
| parent | 941eeb3ab5dbf0bbc23f60b43d9361b7b52969a5 (diff) | |
Move playback content store handling into ContentStore.
Then it's a singleton in the backend rather then being owned by a part
of the GUI.
| -rw-r--r-- | src/lib/content_store.cc | 132 | ||||
| -rw-r--r-- | src/lib/content_store.h | 23 | ||||
| -rw-r--r-- | src/lib/wscript | 1 | ||||
| -rw-r--r-- | src/tools/dcpomatic_playlist.cc | 15 | ||||
| -rw-r--r-- | src/wx/content_view.cc | 81 | ||||
| -rw-r--r-- | src/wx/content_view.h | 6 | ||||
| -rw-r--r-- | src/wx/playlist_controls.cc | 2 |
7 files changed, 177 insertions, 83 deletions
diff --git a/src/lib/content_store.cc b/src/lib/content_store.cc new file mode 100644 index 000000000..10f3fff04 --- /dev/null +++ b/src/lib/content_store.cc @@ -0,0 +1,132 @@ +/* + Copyright (C) 2024 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + DCP-o-matic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "config.h" +#include "content_factory.h" +#include "content_store.h" +#include "cross.h" +#include "dcp_content.h" +#include "examine_content_job.h" +#include "job_manager.h" +#include "util.h" +#include <dcp/exceptions.h> +#include <dcp/filesystem.h> + + +using std::make_shared; +using std::pair; +using std::shared_ptr; +using std::string; +using std::vector; + + +ContentStore* ContentStore::_instance = nullptr; + + +vector<pair<string, string>> +ContentStore::update(std::function<bool()> pulse) +{ + _content.clear(); + auto dir = Config::instance()->player_content_directory(); + if (!dir || !dcp::filesystem::is_directory(*dir)) { + return {}; + } + + auto jm = JobManager::instance(); + + vector<shared_ptr<ExamineContentJob>> jobs; + + for (auto i: boost::filesystem::directory_iterator(*dir)) { + try { + pulse(); + + shared_ptr<Content> content; + if (is_directory(i) && contains_assetmap(i)) { + content = make_shared<DCPContent>(i); + } else if (i.path().extension() == ".mp4") { + auto all_content = content_factory(i); + if (!all_content.empty()) { + content = all_content[0]; + } + } + + if (content) { + auto job = make_shared<ExamineContentJob>(shared_ptr<Film>(), content, true); + jm->add(job); + jobs.push_back(job); + } + } catch (boost::filesystem::filesystem_error& e) { + /* Never mind */ + } catch (dcp::ReadError& e) { + /* Never mind */ + } + } + + while (jm->work_to_do()) { + if (!pulse()) { + /* user pressed cancel */ + for (auto i: jm->get()) { + i->cancel(); + } + return {}; + } + dcpomatic_sleep_seconds(1); + } + + /* Add content from successful jobs and report errors */ + vector<pair<string, string>> errors; + for (auto i: jobs) { + if (i->finished_in_error()) { + errors.push_back({String::compose("%1.\n", i->error_summary()), i->error_details()}); + } else { + _content.push_back(i->content()); + } + } + + return errors; +} + + +shared_ptr<Content> +ContentStore::get(string digest) const +{ + auto iter = std::find_if(_content.begin(), _content.end(), [digest](shared_ptr<const Content> content) { + return content->digest() == digest; + }); + + if (iter == _content.end()) { + return {}; + } + + return *iter; +} + + +ContentStore* +ContentStore::instance() +{ + if (!_instance) { + _instance = new ContentStore(); + } + + return _instance; +} + diff --git a/src/lib/content_store.h b/src/lib/content_store.h index d5a6336a5..dcf31b5a4 100644 --- a/src/lib/content_store.h +++ b/src/lib/content_store.h @@ -19,16 +19,35 @@ */ +#include <functional> #include <memory> +#include <vector> class Content; /** @class ContentStore - * @brief Parent for classes which store content and can return content with a given digest. + * @brief Class to maintain details of what content we have available to play */ class ContentStore { public: - virtual std::shared_ptr<Content> get (std::string digest) const = 0; + std::shared_ptr<Content> get(std::string digest) const; + + /** Examine content in the configured directory and update our list. + * @param pulse Called every so often to indicate progress. Return false to cancel the scan. + * @return Errors (first of the pair is the summary, second is the detail). + */ + std::vector<std::pair<std::string, std::string>> update(std::function<bool()> pulse); + + static ContentStore* instance(); + + std::vector<std::shared_ptr<Content>> const& all() const { + return _content; + } + +private: + std::vector<std::shared_ptr<Content>> _content; + + static ContentStore* _instance; }; diff --git a/src/lib/wscript b/src/lib/wscript index 2855041b2..ffab6404e 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -57,6 +57,7 @@ sources = """ config.cc content.cc content_factory.cc + content_store.cc combine_dcp_job.cc copy_dcp_details_to_film.cc cpu_j2k_encoder_thread.cc diff --git a/src/tools/dcpomatic_playlist.cc b/src/tools/dcpomatic_playlist.cc index 7798c3278..11d162365 100644 --- a/src/tools/dcpomatic_playlist.cc +++ b/src/tools/dcpomatic_playlist.cc @@ -71,7 +71,7 @@ save_playlist(shared_ptr<const SPL> playlist) } -class ContentDialog : public wxDialog, public ContentStore +class ContentDialog : public wxDialog { public: ContentDialog (wxWindow* parent) @@ -101,11 +101,6 @@ public: return _content_view->selected (); } - shared_ptr<Content> get (string digest) const override - { - return _content_view->get (digest); - } - private: ContentView* _content_view; boost::signals2::scoped_connection _config_changed_connection; @@ -116,9 +111,8 @@ private: class PlaylistList { public: - PlaylistList (wxPanel* parent, ContentStore* content_store) + PlaylistList(wxPanel* parent) : _sizer (new wxBoxSizer(wxVERTICAL)) - , _content_store (content_store) , _parent(parent) { auto label = new wxStaticText (parent, wxID_ANY, wxEmptyString); @@ -228,7 +222,7 @@ private: for (auto i: dcp::filesystem::directory_iterator(*path)) { auto spl = make_shared<SignalSPL>(); try { - spl->read (i, _content_store); + spl->read(i, ContentStore::instance()); add_playlist_to_model (spl); } catch (...) {} } @@ -299,7 +293,6 @@ private: wxButton* _new; wxButton* _delete; vector<shared_ptr<SignalSPL>> _playlists; - ContentStore* _content_store; wxWindow* _parent; }; @@ -518,7 +511,7 @@ public: auto overall_panel = new wxPanel (this, wxID_ANY); auto sizer = new wxBoxSizer (wxVERTICAL); - _playlist_list = new PlaylistList (overall_panel, _content_dialog); + _playlist_list = new PlaylistList(overall_panel); _playlist_content = new PlaylistContent (overall_panel, _content_dialog); sizer->Add (_playlist_list->sizer()); diff --git a/src/wx/content_view.cc b/src/wx/content_view.cc index 6a5a3793d..fb0afd976 100644 --- a/src/wx/content_view.cc +++ b/src/wx/content_view.cc @@ -70,79 +70,41 @@ ContentView::selected () const return {}; } - DCPOMATIC_ASSERT (s < int(_content.size())); - return _content[s]; + DCPOMATIC_ASSERT(s < int(_content_digests.size())); + return ContentStore::instance()->get(_content_digests[s]); } void ContentView::update () { - using namespace boost::filesystem; - - DeleteAllItems (); - _content.clear (); auto dir = Config::instance()->player_content_directory(); if (!dir || !dcp::filesystem::is_directory(*dir)) { dir = home_directory (); } wxProgressDialog progress(variant::wx::dcpomatic(), _("Reading content directory")); - auto jm = JobManager::instance (); - - list<shared_ptr<ExamineContentJob>> jobs; - - for (auto i: directory_iterator(*dir)) { - try { - progress.Pulse (); - - shared_ptr<Content> content; - if (is_directory(i) && contains_assetmap(i)) { - content = make_shared<DCPContent>(i); - } else if (i.path().extension() == ".mp4") { - auto all_content = content_factory(i); - if (!all_content.empty()) { - content = all_content[0]; - } - } - - if (content) { - auto job = make_shared<ExamineContentJob>(shared_ptr<Film>(), content, false); - jm->add (job); - jobs.push_back (job); - } - } catch (boost::filesystem::filesystem_error& e) { - /* Never mind */ - } catch (dcp::ReadError& e) { - /* Never mind */ - } - } - while (jm->work_to_do()) { - if (!progress.Pulse()) { - /* user pressed cancel */ - for (auto i: jm->get()) { - i->cancel(); - } - return; - } - dcpomatic_sleep_seconds (1); + auto store = ContentStore::instance(); + + auto errors = store->update([&progress]() { + return progress.Pulse(); + }); + + DeleteAllItems (); + _content_digests.clear(); + for (auto content: store->all()) { + add(content); } - /* Add content from successful jobs and report errors */ - for (auto i: jobs) { - if (i->finished_in_error()) { - error_dialog(this, std_to_wx(i->error_summary()) + char_to_wx(".\n"), std_to_wx(i->error_details())); - } else { - add (i->content()); - _content.push_back (i->content()); - } + for (auto error: errors) { + error_dialog(this, std_to_wx(error.first), std_to_wx(error.second)); } } void -ContentView::add (shared_ptr<Content> content) +ContentView::add(shared_ptr<Content> content) { int const N = GetItemCount(); @@ -166,17 +128,6 @@ ContentView::add (shared_ptr<Content> content) it.SetColumn(2); it.SetText(std_to_wx(content->summary())); SetItem(it); -} - - -shared_ptr<Content> -ContentView::get (string digest) const -{ - for (auto i: _content) { - if (i->digest() == digest) { - return i; - } - } - return {}; + _content_digests.push_back(content->digest()); } diff --git a/src/wx/content_view.h b/src/wx/content_view.h index 16ff5c463..65f806340 100644 --- a/src/wx/content_view.h +++ b/src/wx/content_view.h @@ -31,7 +31,7 @@ class Content; class Film; -class ContentView : public wxListCtrl, public ContentStore +class ContentView : public wxListCtrl { public: ContentView (wxWindow* parent); @@ -39,11 +39,9 @@ public: std::shared_ptr<Content> selected () const; void update (); - std::shared_ptr<Content> get (std::string digest) const override; - private: void add (std::shared_ptr<Content> content); std::weak_ptr<Film> _film; - std::vector<std::shared_ptr<Content>> _content; + std::vector<std::string> _content_digests; }; diff --git a/src/wx/playlist_controls.cc b/src/wx/playlist_controls.cc index 917edfd23..454f441dc 100644 --- a/src/wx/playlist_controls.cc +++ b/src/wx/playlist_controls.cc @@ -265,7 +265,7 @@ PlaylistControls::update_playlist_directory () try { if (is_regular_file(i->path()) && i->path().extension() == ".xml") { SPL spl; - spl.read (i->path(), _content_view); + spl.read(i->path(), ContentStore::instance()); _playlists.push_back (spl); } } catch (exception& e) { |
