--- /dev/null
+/*
+ Copyright (C) 2018 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 <boost/shared_ptr.hpp>
+
+class Content;
+
+/** @class ContentStore
+ * @brief Parent for classes which store content and can return content with a given digest.
+ */
+class ContentStore
+{
+public:
+ virtual boost::shared_ptr<Content> get (std::string digest) const = 0;
+};
--- /dev/null
+/*
+ Copyright (C) 2018 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 "spl.h"
+#include "content_store.h"
+#include <libcxml/cxml.h>
+#include <libxml++/libxml++.h>
+#include <boost/foreach.hpp>
+
+using boost::shared_ptr;
+
+bool
+SPL::read (boost::filesystem::path path, ContentStore* store)
+{
+ _spl.clear ();
+ cxml::Document doc ("SPL");
+ doc.read_file (path);
+ bool missing = false;
+ BOOST_FOREACH (cxml::ConstNodePtr i, doc.node_children("Entry")) {
+ shared_ptr<Content> c = store->get(i->string_child("Digest"));
+ if (c) {
+ add (SPLEntry(c, i));
+ } else {
+ missing = true;
+ }
+ }
+
+ _name = path.filename().string();
+ return missing;
+}
+
+void
+SPL::write (boost::filesystem::path path) const
+{
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("SPL");
+ BOOST_FOREACH (SPLEntry i, _spl) {
+ i.as_xml (root->add_child("Entry"));
+ }
+ doc.write_to_file_formatted (path.string());
+}
--- /dev/null
+/*
+ Copyright (C) 2018 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 "spl_entry.h"
+
+class ContentStore;
+
+class SPL
+{
+public:
+ void add (SPLEntry e) {
+ _spl.push_back (e);
+ }
+
+ void remove (std::size_t index) {
+ _spl.erase (_spl.begin() + index);
+ }
+
+ std::vector<SPLEntry> const & get () const {
+ return _spl;
+ }
+
+ SPLEntry & operator[] (std::size_t index) {
+ return _spl[index];
+ }
+
+ SPLEntry const & operator[] (std::size_t index) const {
+ return _spl[index];
+ }
+
+ bool read (boost::filesystem::path path, ContentStore* store);
+ void write (boost::filesystem::path path) const;
+
+ std::string name () const {
+ return _name;
+ }
+
+private:
+ std::string _name;
+ std::vector<SPLEntry> _spl;
+};
--- /dev/null
+/*
+ Copyright (C) 2018 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 "spl_entry.h"
+#include "dcp_content.h"
+#include "dcpomatic_assert.h"
+#include <libxml++/libxml++.h>
+
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+SPLEntry::SPLEntry (shared_ptr<Content> content)
+ : skippable (false)
+ , disable_timeline (false)
+ , stop_after_play (false)
+{
+ construct (content);
+}
+
+SPLEntry::SPLEntry (shared_ptr<Content> content, cxml::ConstNodePtr node)
+ : skippable (node->bool_child("Skippable"))
+ , disable_timeline (node->bool_child("DisableTimeline"))
+ , stop_after_play (node->bool_child("StopAfterPlay"))
+{
+ construct (content);
+}
+
+void
+SPLEntry::construct (shared_ptr<Content> c)
+{
+ content = c;
+ shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (content);
+ digest = content->digest ();
+ if (dcp) {
+ name = dcp->name ();
+ DCPOMATIC_ASSERT (dcp->cpl());
+ id = *dcp->cpl();
+ kind = dcp->content_kind().get_value_or(dcp::FEATURE);
+ type = DCP;
+ encrypted = dcp->encrypted ();
+ } else {
+ name = content->path(0).filename().string();
+ type = ECINEMA;
+ kind = dcp::FEATURE;
+ }
+}
+
+void
+SPLEntry::as_xml (xmlpp::Element* e)
+{
+ e->add_child("Digest")->add_child_text(digest);
+ e->add_child("Skippable")->add_child_text(skippable ? "1" : "0");
+ e->add_child("DisableTimeline")->add_child_text(disable_timeline ? "1" : "0");
+ e->add_child("StopAfterPlay")->add_child_text(stop_after_play ? "1" : "0");
+}
--- /dev/null
+/*
+ Copyright (C) 2018 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/>.
+
+*/
+
+#ifndef DCPOMATIC_SPL_ENTRY_H
+#define DCPOMATIC_SPL_ENTRY_H
+
+#include <libcxml/cxml.h>
+#include <dcp/types.h>
+#include <boost/shared_ptr.hpp>
+
+namespace xmlpp {
+ class Element;
+}
+
+class Content;
+
+class SPLEntry
+{
+public:
+ SPLEntry (boost::shared_ptr<Content> content);
+ SPLEntry (boost::shared_ptr<Content> content, cxml::ConstNodePtr node);
+
+ void as_xml (xmlpp::Element* e);
+
+ boost::shared_ptr<Content> content;
+ std::string name;
+ /** Digest of this content */
+ std::string digest;
+ /** CPL ID or something else for MP4 (?) */
+ std::string id;
+ dcp::ContentKind kind;
+ enum Type {
+ DCP,
+ ECINEMA
+ };
+ Type type;
+ bool encrypted;
+ bool skippable;
+ bool disable_timeline;
+ bool stop_after_play;
+
+private:
+ void construct (boost::shared_ptr<Content> content);
+};
+
+#endif
send_problem_report_job.cc
server.cc
shuffler.cc
+ spl.cc
+ spl_entry.cc
string_log_entry.cc
string_text_file.cc
string_text_file_content.cc
#include "../lib/cross.h"
#include "../lib/film.h"
#include "../lib/dcp_content.h"
+#include "../lib/spl_entry.h"
+#include "../lib/spl.h"
#include <wx/wx.h>
#include <wx/listctrl.h>
#include <wx/imaglist.h>
using boost::bind;
using boost::dynamic_pointer_cast;
-class PlaylistEntry
-{
-public:
- PlaylistEntry (boost::shared_ptr<Content> content)
- : skippable (false)
- , disable_timeline (false)
- , stop_after_play (false)
- {
- construct (content);
- }
-
- PlaylistEntry (boost::shared_ptr<Content> content, cxml::ConstNodePtr node)
- : skippable (node->bool_child("Skippable"))
- , disable_timeline (node->bool_child("DisableTimeline"))
- , stop_after_play (node->bool_child("StopAfterPlay"))
- {
- construct (content);
- }
-
- void construct (shared_ptr<Content> content)
- {
- shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (content);
- digest = content->digest ();
- if (dcp) {
- name = dcp->name ();
- DCPOMATIC_ASSERT (dcp->cpl());
- id = *dcp->cpl();
- kind = dcp->content_kind().get_value_or(dcp::FEATURE);
- type = DCP;
- encrypted = dcp->encrypted ();
- } else {
- name = content->path(0).filename().string();
- type = ECINEMA;
- kind = dcp::FEATURE;
- }
- }
-
- void as_xml (xmlpp::Element* e)
- {
- e->add_child("Digest")->add_child_text(digest);
- e->add_child("Skippable")->add_child_text(skippable ? "1" : "0");
- e->add_child("DisableTimeline")->add_child_text(disable_timeline ? "1" : "0");
- e->add_child("StopAfterPlay")->add_child_text(stop_after_play ? "1" : "0");
- }
-
- std::string name;
- /** Digest of this content */
- std::string digest;
- /** CPL ID or something else for MP4 (?) */
- std::string id;
- dcp::ContentKind kind;
- enum Type {
- DCP,
- ECINEMA
- };
- Type type;
- bool encrypted;
- bool skippable;
- bool disable_timeline;
- bool stop_after_play;
-};
-
-class ContentDialog : public wxDialog
+class ContentDialog : public wxDialog, public ContentStore
{
public:
ContentDialog (wxWindow* parent, weak_ptr<Film> film)
private:
- void add (PlaylistEntry e)
+ void add (SPLEntry e)
{
wxListItem item;
item.SetId (_list->GetItemCount());
long const N = _list->InsertItem (item);
set_item (N, e);
- _playlist.push_back (e);
+ _playlist.add (e);
}
void selection_changed ()
setup_sensitivity ();
}
- void set_item (long N, PlaylistEntry e)
+ void set_item (long N, SPLEntry e)
{
_list->SetItem (N, 0, std_to_wx(e.name));
_list->SetItem (N, 1, std_to_wx(e.id));
_list->SetItem (N, 2, std_to_wx(dcp::content_kind_to_string(e.kind)));
- _list->SetItem (N, 3, e.type == PlaylistEntry::DCP ? _("DCP") : _("E-cinema"));
+ _list->SetItem (N, 3, e.type == SPLEntry::DCP ? _("DCP") : _("E-cinema"));
_list->SetItem (N, 4, e.encrypted ? _("Y") : _("N"));
_list->SetItem (N, COLUMN_SKIPPABLE, wxEmptyString, e.skippable ? 0 : 1);
_list->SetItem (N, COLUMN_DISABLE_TIMELINE, wxEmptyString, e.disable_timeline ? 0 : 1);
if (r == wxID_OK) {
shared_ptr<Content> content = _content_dialog->selected ();
if (content) {
- add (PlaylistEntry(content));
+ add (SPLEntry(content));
}
}
}
return;
}
- PlaylistEntry tmp = _playlist[s];
+ SPLEntry tmp = _playlist[s];
_playlist[s] = _playlist[s-1];
_playlist[s-1] = tmp;
return;
}
- PlaylistEntry tmp = _playlist[s];
+ SPLEntry tmp = _playlist[s];
_playlist[s] = _playlist[s+1];
_playlist[s+1] = tmp;
return;
}
- _playlist.erase (_playlist.begin() + s);
+ _playlist.remove (s);
_list->DeleteItem (s);
}
{
wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (d->ShowModal() == wxID_OK) {
- xmlpp::Document doc;
- xmlpp::Element* root = doc.create_root_node ("SPL");
- BOOST_FOREACH (PlaylistEntry i, _playlist) {
- i.as_xml (root->add_child("Entry"));
- }
- doc.write_to_file_formatted (wx_to_std(d->GetPath()));
+ _playlist.write (wx_to_std(d->GetPath()));
}
}
wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"));
if (d->ShowModal() == wxID_OK) {
_list->DeleteAllItems ();
- _playlist.clear ();
- cxml::Document doc ("SPL");
- doc.read_file (wx_to_std(d->GetPath()));
- bool missing = false;
- BOOST_FOREACH (cxml::ConstNodePtr i, doc.node_children("Entry")) {
- shared_ptr<Content> c = _content_dialog->get(i->string_child("Digest"));
- if (c) {
- add (PlaylistEntry(c, i));
- } else {
- missing = true;
- }
- }
- if (missing) {
+ if (_playlist.read (wx_to_std(d->GetPath()), _content_dialog)) {
error_dialog (this, _("Some content in this playlist was not found."));
}
}
wxButton* _save;
wxButton* _load;
boost::shared_ptr<Film> _film;
- std::vector<PlaylistEntry> _playlist;
+ SPL _playlist;
ContentDialog* _content_dialog;
enum {
*/
+#include "lib/content_store.h"
#include <wx/listctrl.h>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
class Content;
class Film;
-class ContentView : public wxListCtrl
+class ContentView : public wxListCtrl, public ContentStore
{
public:
ContentView (wxWindow* parent, boost::weak_ptr<Film> film);
e_sizer->Add (left_sizer, 1, wxALL | wxEXPAND, DCPOMATIC_SIZER_GAP);
_current_spl_view = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_NO_HEADER);
+ _current_spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 500);
_current_spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 80);
- _current_spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 80);
- _current_spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 580);
e_sizer->Add (_current_spl_view, 1, wxALL | wxEXPAND, DCPOMATIC_SIZER_GAP);
_v_sizer->Add (e_sizer, 1, wxEXPAND);
_forward_button->Bind (wxEVT_LEFT_DOWN, boost::bind(&Controls::forward_clicked, this, _1));
_frame_number->Bind (wxEVT_LEFT_DOWN, boost::bind(&Controls::frame_number_clicked, this));
_timecode->Bind (wxEVT_LEFT_DOWN, boost::bind(&Controls::timecode_clicked, this));
- _content_view->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&Controls::setup_sensitivity, this));
- _content_view->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&Controls::setup_sensitivity, this));
if (_jump_to_selected) {
_jump_to_selected->Bind (wxEVT_CHECKBOX, boost::bind (&Controls::jump_to_selected_clicked, this));
_jump_to_selected->SetValue (Config::instance()->jump_to_selected ());
}
+ _spl_view->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&Controls::spl_selection_changed, this));
+ _spl_view->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&Controls::spl_selection_changed, this));
_viewer->PositionChanged.connect (boost::bind(&Controls::position_changed, this));
_viewer->Started.connect (boost::bind(&Controls::started, this));
config_changed (Config::OTHER);
}
+void
+Controls::spl_selection_changed ()
+{
+ long int selected = _spl_view->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ if (selected == -1) {
+ _current_spl_view->DeleteAllItems ();
+ return;
+ }
+
+ shared_ptr<Film> film (new Film(optional<boost::filesystem::path>()));
+
+ int N = 0;
+ BOOST_FOREACH (SPLEntry i, _playlists[selected].get()) {
+ wxListItem it;
+ it.SetId (N);
+ it.SetColumn (0);
+ it.SetText (std_to_wx(i.name));
+ _current_spl_view->InsertItem (it);
+ film->add_content (i.content);
+ ++N;
+ }
+
+ _viewer->set_film (film);
+}
+
void
Controls::config_changed (int property)
{
_viewer->set_outline_content (_outline_content->GetValue());
}
-void
-Controls::film_change (ChangeType type, Film::Property p)
-{
- if (type != CHANGE_TYPE_DONE) {
- return;
- }
-
- if (p == Film::CONTENT || p == Film::THREE_D) {
- setup_sensitivity ();
- }
-}
-
/** @param page true if this was a PAGEUP/PAGEDOWN event for which we won't receive a THUMBRELEASE */
void
Controls::slider_moved (bool page)
update_position_slider ();
update_position_label ();
- if (_film) {
- _film->Change.connect (boost::bind (&Controls::film_change, this, _1, _2));
- }
-
_content_view->set_film (film);
_content_view->update ();
}
}
void
-Controls::add_playlist_to_list (shared_ptr<Film> film)
+Controls::add_playlist_to_list (SPL spl)
{
int const N = _spl_view->GetItemCount();
wxListItem it;
it.SetId(N);
it.SetColumn(0);
- it.SetText (std_to_wx(film->name()));
+ it.SetText (std_to_wx(spl.name()));
_spl_view->InsertItem (it);
}
for (directory_iterator i = directory_iterator(*dir); i != directory_iterator(); ++i) {
try {
- shared_ptr<Film> film (new Film(optional<path>()));
- film->read_metadata (i->path());
- _playlists.push_back (film);
- add_playlist_to_list (film);
+ if (is_regular_file(i->path()) && i->path().extension() == ".xml") {
+ SPL spl;
+ spl.read (i->path(), _content_view);
+ _playlists.push_back (spl);
+ add_playlist_to_list (spl);
+ }
} catch (exception& e) {
/* Never mind */
}
#include "lib/dcpomatic_time.h"
#include "lib/types.h"
#include "lib/film.h"
+#include "lib/spl.h"
#include <wx/wx.h>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
class wxToggleButton;
class wxListCtrl;
class ContentView;
+class SPL;
namespace dcp {
class CPL;
void active_jobs_changed (boost::optional<std::string>);
DCPTime nudge_amount (wxKeyboardState& ev);
void image_changed (boost::weak_ptr<PlayerVideo>);
- void film_change (ChangeType type, Film::Property p);
void outline_content_changed ();
void eye_changed ();
void position_changed ();
void update_content_directory ();
void update_playlist_directory ();
void config_changed (int property);
+ void spl_selection_changed ();
typedef std::pair<boost::shared_ptr<dcp::CPL>, boost::filesystem::path> CPL;
void pause_clicked ();
void stop_clicked ();
#endif
- void add_playlist_to_list (boost::shared_ptr<Film> film);
+ void add_playlist_to_list (SPL spl);
boost::shared_ptr<Film> _film;
boost::shared_ptr<FilmViewer> _viewer;
wxListCtrl* _spl_view;
wxListCtrl* _current_spl_view;
wxTextCtrl* _log;
- std::vector<boost::shared_ptr<Film> > _playlists;
+ std::vector<SPL> _playlists;
wxSlider* _slider;
wxButton* _rewind_button;
wxButton* _back_button;