diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-10-14 23:34:36 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2026-02-16 01:20:17 +0100 |
| commit | 144590d21b70f0239612b82f7c1d422533c64fbb (patch) | |
| tree | ca7d399ae53929ed469f8dfba430b51e6cdf6733 /src | |
| parent | 1deb9a98a0a6204720c2e701b39c9e3f43a21873 (diff) | |
Move content store handling into ContentStore rather than it being done in the GUI.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/content_store.cc | 163 | ||||
| -rw-r--r-- | src/lib/content_store.h | 25 | ||||
| -rw-r--r-- | src/lib/wscript | 1 | ||||
| -rw-r--r-- | src/tools/dcpomatic_playlist.cc | 20 | ||||
| -rw-r--r-- | src/wx/content_view.cc | 106 | ||||
| -rw-r--r-- | src/wx/content_view.h | 5 | ||||
| -rw-r--r-- | src/wx/playlist_controls.cc | 2 |
7 files changed, 205 insertions, 117 deletions
diff --git a/src/lib/content_store.cc b/src/lib/content_store.cc new file mode 100644 index 000000000..35fde3c8e --- /dev/null +++ b/src/lib/content_store.cc @@ -0,0 +1,163 @@ +/* + Copyright (C) 2025 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/cpl.h> +#include <dcp/exceptions.h> +#include <dcp/filesystem.h> +#include <dcp/search.h> + + +using std::dynamic_pointer_cast; +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; + + auto examine = [&](shared_ptr<Content> content) { + auto job = make_shared<ExamineContentJob>(vector<shared_ptr<Content>>{content}, true); + jm->add(job); + jobs.push_back(job); + }; + + for (auto i: boost::filesystem::directory_iterator(*dir)) { + try { + pulse(); + + if (is_directory(i) && contains_assetmap(i)) { + auto dcp = make_shared<DCPContent>(i); + /* Add a Content for each CPL in this DCP, so we can choose CPLs to play + * rather than DCPs. + */ + for (auto cpl: dcp::find_and_resolve_cpls(dcp->directories(), true)) { + auto copy = dynamic_pointer_cast<DCPContent>(dcp->clone()); + copy->set_cpl(cpl->id()); + examine(copy); + } + } else if (i.path().extension() == ".mp4") { + auto all_content = content_factory(i); + if (!all_content.empty()) { + examine(all_content[0]); + } + } + } 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({fmt::format("{}.\n", i->error_summary()), i->error_details()}); + } else { + _content.push_back(i->content().front()); + } + } + + return errors; +} + + +shared_ptr<Content> +ContentStore::get_by_digest(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; +} + + +shared_ptr<Content> +ContentStore::get_by_cpl_id(string id) const +{ + auto iter = std::find_if(_content.begin(), _content.end(), [id](shared_ptr<const Content> content) { + if (auto dcp = dynamic_pointer_cast<const DCPContent>(content)) { + for (auto cpl: dcp::find_and_resolve_cpls(dcp->directories(), true)) { + if (cpl->id() == id) { + return true; + } + } + } + return false; + }); + + 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 c615a856e..1b3afc85d 100644 --- a/src/lib/content_store.h +++ b/src/lib/content_store.h @@ -19,18 +19,37 @@ */ +#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 or CPL ID. + * @brief Class to maintain details of what content we have available to play */ class ContentStore { public: - virtual std::shared_ptr<Content> get_by_digest(std::string digest) const = 0; - virtual std::shared_ptr<Content> get_by_cpl_id(std::string id) const = 0; + std::shared_ptr<Content> get_by_digest(std::string digest) const; + std::shared_ptr<Content> get_by_cpl_id(std::string id) 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 8e4b4d783..a6130e41c 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 cover_sheet.cc diff --git a/src/tools/dcpomatic_playlist.cc b/src/tools/dcpomatic_playlist.cc index f90af4a29..6397b1e8d 100644 --- a/src/tools/dcpomatic_playlist.cc +++ b/src/tools/dcpomatic_playlist.cc @@ -73,7 +73,7 @@ save_playlist(shared_ptr<const SPL> playlist) } -class ContentDialog : public wxDialog, public ContentStore +class ContentDialog : public wxDialog { public: ContentDialog(wxWindow* parent) @@ -103,16 +103,6 @@ public: return _content_view->selected(); } - shared_ptr<Content> get_by_digest(string digest) const override - { - return _content_view->get_by_digest(digest); - } - - shared_ptr<Content> get_by_cpl_id(string cpl_id) const override - { - return _content_view->get_by_cpl_id(cpl_id); - } - private: ContentView* _content_view; boost::signals2::scoped_connection _config_changed_connection; @@ -123,9 +113,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); @@ -235,7 +224,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 (...) {} } @@ -306,7 +295,6 @@ private: wxButton* _new; wxButton* _delete; vector<shared_ptr<SignalSPL>> _playlists; - ContentStore* _content_store; wxWindow* _parent; }; @@ -548,7 +536,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 438f53785..ce7e9693e 100644 --- a/src/wx/content_view.cc +++ b/src/wx/content_view.cc @@ -81,76 +81,27 @@ ContentView::selected () const 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>(vector<shared_ptr<Content>>{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.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 { - for (auto c: i->content()) { - if (auto dcp = dynamic_pointer_cast<DCPContent>(c)) { - for (auto cpl: dcp::find_and_resolve_cpls(dcp->directories(), true)) { - auto copy = dynamic_pointer_cast<DCPContent>(dcp->clone()); - copy->set_cpl(cpl->id()); - add(copy); - _content.push_back(copy); - } - } else { - add(c); - _content.push_back(c); - } - } - } + for (auto error: errors) { + error_dialog(this, std_to_wx(error.first), std_to_wx(error.second)); } } @@ -180,39 +131,8 @@ ContentView::add (shared_ptr<Content> content) it.SetColumn(2); it.SetText(std_to_wx(content->summary())); SetItem(it); -} - -shared_ptr<Content> -ContentView::get_by_digest(string digest) const -{ - auto iter = std::find_if(_content.begin(), _content.end(), [digest](shared_ptr<const Content> c) { return c->digest() == digest; }); - if (iter == _content.end()) { - return {}; - } - - return *iter; + _content.push_back(content); } -shared_ptr<Content> -ContentView::get_by_cpl_id(string cpl_id) const -{ - auto iter = std::find_if( - _content.begin(), - _content.end(), - [cpl_id](shared_ptr<const Content> c) { - if (auto dcp = dynamic_pointer_cast<const DCPContent>(c)) { - if (dcp->cpl() && *dcp->cpl() == cpl_id) { - return true; - } - } - return false; - }); - - if (iter == _content.end()) { - return {}; - } - - return *iter; -} diff --git a/src/wx/content_view.h b/src/wx/content_view.h index 703878d46..305a385dc 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,9 +39,6 @@ public: std::shared_ptr<Content> selected () const; void update (); - std::shared_ptr<Content> get_by_digest(std::string digest) const override; - std::shared_ptr<Content> get_by_cpl_id(std::string cpl_id) const override; - private: void add (std::shared_ptr<Content> content); diff --git a/src/wx/playlist_controls.cc b/src/wx/playlist_controls.cc index d9914462c..231df5374 100644 --- a/src/wx/playlist_controls.cc +++ b/src/wx/playlist_controls.cc @@ -276,7 +276,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) { |
