Basic save/load of playlists.
[dcpomatic.git] / src / tools / dcpomatic_playlist.cc
index 04beb0250f2962cd55d10c4c284f25d9bed86835..43cb70550c73a0ec4861aa0ce606dc5b06728639 100644 (file)
@@ -32,6 +32,7 @@
 
 using std::exception;
 using std::cout;
+using std::string;
 using boost::optional;
 using boost::shared_ptr;
 using boost::weak_ptr;
@@ -45,22 +46,49 @@ public:
                : 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 ();
-                       cpl_id = dcp->cpl().get_value_or("");
+                       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;
-       std::string cpl_id;
+       /** Digest of this content */
+       std::string digest;
+       /** CPL ID or something else for MP4 (?) */
+       std::string id;
        dcp::ContentKind kind;
        enum Type {
                DCP,
@@ -100,6 +128,11 @@ public:
                return _content_view->selected ();
        }
 
+       shared_ptr<Content> get (string digest) const
+       {
+               return _content_view->get (digest);
+       }
+
 private:
        ContentView* _content_view;
 };
@@ -175,6 +208,8 @@ public:
                _down->Bind (wxEVT_BUTTON, bind(&DOMFrame::down_clicked, this));
                _add->Bind (wxEVT_BUTTON, bind(&DOMFrame::add_clicked, this));
                _remove->Bind (wxEVT_BUTTON, bind(&DOMFrame::remove_clicked, this));
+               _save->Bind (wxEVT_BUTTON, bind(&DOMFrame::save_clicked, this));
+               _load->Bind (wxEVT_BUTTON, bind(&DOMFrame::load_clicked, this));
 
                setup_sensitivity ();
        }
@@ -198,7 +233,7 @@ private:
        void set_item (long N, PlaylistEntry e)
        {
                _list->SetItem (N, 0, std_to_wx(e.name));
-               _list->SetItem (N, 1, std_to_wx(e.cpl_id));
+               _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, 4, e.encrypted ? _("Y") : _("N"));
@@ -302,6 +337,42 @@ private:
                _list->DeleteItem (s);
        }
 
+       void save_clicked ()
+       {
+               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()));
+               }
+       }
+
+       void load_clicked ()
+       {
+               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) {
+                               error_dialog (this, _("Some content in this playlist was not found."));
+                       }
+               }
+       }
+
        wxListCtrl* _list;
        wxButton* _up;
        wxButton* _down;