mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-08 15:54:57 +01:00
Merge branch 'export-dialog' into cairocanvas
Fix merge conflicts in: gtk2_ardour/export_range_markers_dialog.cc gtk2_ardour/wscript libs/ardour/ardour/export_handler.h libs/ardour/system_exec.cc libs/pbd/pbd/system_exec.h libs/pbd/system_exec.cc
This commit is contained in:
commit
5399425f53
29 changed files with 992 additions and 367 deletions
30
export/Soundcloud upload (lossless).format
Normal file
30
export/Soundcloud upload (lossless).format
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ExportFormatSpecification name="Soundcloud upload (lossless)" id="14f5b271-86b3-48c6-9ee2-133f46fe06c1" with-cue="false" with-toc="false" upload="true" command="">
|
||||
<Encoding id="F_FLAC" type="T_Sndfile" extension="flac" name="FLAC" has-sample-format="true" channel-limit="8"/>
|
||||
<SampleRate rate="1"/>
|
||||
<SRCQuality quality="SRC_SincBest"/>
|
||||
<EncodingOptions>
|
||||
<Option name="sample-format" value="SF_16"/>
|
||||
<Option name="dithering" value="D_Shaped"/>
|
||||
<Option name="tag-metadata" value="true"/>
|
||||
<Option name="tag-support" value="true"/>
|
||||
<Option name="broadcast-info" value="false"/>
|
||||
</EncodingOptions>
|
||||
<Processing>
|
||||
<Normalize enabled="false" target="1"/>
|
||||
<Silence>
|
||||
<Start>
|
||||
<Trim enabled="false"/>
|
||||
<Add enabled="false">
|
||||
<Duration format="Timecode" hours="0" minutes="0" seconds="0" frames="0"/>
|
||||
</Add>
|
||||
</Start>
|
||||
<End>
|
||||
<Trim enabled="false"/>
|
||||
<Add enabled="false">
|
||||
<Duration format="Timecode" hours="0" minutes="0" seconds="0" frames="0"/>
|
||||
</Add>
|
||||
</End>
|
||||
</Silence>
|
||||
</Processing>
|
||||
</ExportFormatSpecification>
|
||||
30
export/Soundcloud upload (lossy).format
Normal file
30
export/Soundcloud upload (lossy).format
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ExportFormatSpecification name="Soundcloud upload (lossy)" id="a42eb2fe-2470-4aa9-8027-798ba625592a" with-cue="false" with-toc="false" upload="true" command="">
|
||||
<Encoding id="F_Ogg" type="T_Sndfile" extension="ogg" name="Ogg Vorbis" has-sample-format="false" channel-limit="256"/>
|
||||
<SampleRate rate="44100"/>
|
||||
<SRCQuality quality="SRC_SincBest"/>
|
||||
<EncodingOptions>
|
||||
<Option name="sample-format" value="SF_Vorbis"/>
|
||||
<Option name="dithering" value="D_Shaped"/>
|
||||
<Option name="tag-metadata" value="true"/>
|
||||
<Option name="tag-support" value="true"/>
|
||||
<Option name="broadcast-info" value="false"/>
|
||||
</EncodingOptions>
|
||||
<Processing>
|
||||
<Normalize enabled="false" target="1"/>
|
||||
<Silence>
|
||||
<Start>
|
||||
<Trim enabled="false"/>
|
||||
<Add enabled="false">
|
||||
<Duration format="Timecode" hours="0" minutes="0" seconds="0" frames="0"/>
|
||||
</Add>
|
||||
</Start>
|
||||
<End>
|
||||
<Trim enabled="false"/>
|
||||
<Add enabled="false">
|
||||
<Duration format="Timecode" hours="0" minutes="0" seconds="0" frames="0"/>
|
||||
</Add>
|
||||
</End>
|
||||
</Silence>
|
||||
</Processing>
|
||||
</ExportFormatSpecification>
|
||||
|
|
@ -458,15 +458,15 @@ RegionExportChannelSelector::RegionExportChannelSelector (ARDOUR::Session * _ses
|
|||
|
||||
raw_button.set_label (string_compose (_("Region contents without fades nor region gain (channels: %1)"), region_chans));
|
||||
raw_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
|
||||
vbox.pack_start (raw_button);
|
||||
vbox.pack_start (raw_button, false, false);
|
||||
|
||||
fades_button.set_label (string_compose (_("Region contents with fades and region gain (channels: %1)"), region_chans));
|
||||
fades_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
|
||||
vbox.pack_start (fades_button);
|
||||
vbox.pack_start (fades_button, false, false);
|
||||
|
||||
processed_button.set_label (string_compose (_("Track output (channels: %1)"), track_chans));
|
||||
processed_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
|
||||
vbox.pack_start (processed_button);
|
||||
vbox.pack_start (processed_button, false, false);
|
||||
|
||||
sync_with_manager();
|
||||
vbox.show_all_children ();
|
||||
|
|
@ -541,7 +541,7 @@ TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * sessio
|
|||
// Options
|
||||
options_box.pack_start(region_contents_button);
|
||||
options_box.pack_start(track_output_button);
|
||||
main_layout.pack_start(options_box);
|
||||
main_layout.pack_start(options_box, false, false);
|
||||
|
||||
// Track scroller
|
||||
track_scroller.add (track_view);
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ class PortExportChannelSelector : public ExportChannelSelector
|
|||
typedef Gtk::TreeModelColumn<Glib::RefPtr<Gtk::ListStore> > ComboCol;
|
||||
ComboCol port_list_col;
|
||||
|
||||
/* Channel struct, that represents the selected port and it's name */
|
||||
/* Channel struct, that represents the selected port and its name */
|
||||
|
||||
struct Channel {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -140,69 +140,27 @@ ExportDialog::init ()
|
|||
progress_widget.hide_all();
|
||||
}
|
||||
|
||||
void
|
||||
ExportDialog::expanded_changed ()
|
||||
{
|
||||
set_resizable(advanced->get_expanded());
|
||||
}
|
||||
|
||||
void
|
||||
ExportDialog::init_gui ()
|
||||
{
|
||||
Gtk::Alignment * preset_align = Gtk::manage (new Gtk::Alignment());
|
||||
preset_align->add (*preset_selector);
|
||||
preset_align->set_padding (0, 12, 0, 0);
|
||||
get_vbox()->pack_start (*preset_align, false, false, 0);
|
||||
|
||||
Gtk::VPaned * advanced_paned = Gtk::manage (new Gtk::VPaned());
|
||||
Gtk::VBox * file_format_selector = Gtk::manage (new Gtk::VBox());
|
||||
file_format_selector->set_homogeneous (false);
|
||||
file_format_selector->pack_start (*preset_align, false, false, 0);
|
||||
file_format_selector->pack_start (*file_notebook, false, false, 0);
|
||||
file_format_selector->pack_start (*soundcloud_selector, false, false, 0);
|
||||
|
||||
Gtk::VBox* timespan_vbox = Gtk::manage (new Gtk::VBox());
|
||||
timespan_vbox->set_spacing (12);
|
||||
timespan_vbox->set_border_width (12);
|
||||
export_notebook.append_page (*file_format_selector, _("File format"));
|
||||
export_notebook.append_page (*timespan_selector, _("Time Span"));
|
||||
export_notebook.append_page (*channel_selector, _("Channels"));
|
||||
|
||||
Gtk::Alignment * timespan_align = Gtk::manage (new Gtk::Alignment());
|
||||
timespan_label = Gtk::manage (new Gtk::Label (_("Time Span"), Gtk::ALIGN_LEFT));
|
||||
timespan_align->add (*timespan_selector);
|
||||
timespan_align->set_padding (0, 0, 18, 0);
|
||||
timespan_vbox->pack_start (*timespan_label, false, false, 0);
|
||||
timespan_vbox->pack_start (*timespan_align, true, true, 0);
|
||||
advanced_paned->pack1(*timespan_vbox, true, false);
|
||||
get_vbox()->pack_start (export_notebook, true, true, 0);
|
||||
get_vbox()->pack_end (warning_widget, false, false, 0);
|
||||
get_vbox()->pack_end (progress_widget, false, false, 0);
|
||||
|
||||
Gtk::VBox* channels_vbox = Gtk::manage (new Gtk::VBox());
|
||||
channels_vbox->set_spacing (12);
|
||||
channels_vbox->set_border_width (12);
|
||||
|
||||
Gtk::Alignment * channels_align = Gtk::manage (new Gtk::Alignment());
|
||||
channels_label = Gtk::manage (new Gtk::Label (_("Channels"), Gtk::ALIGN_LEFT));
|
||||
channels_align->add (*channel_selector);
|
||||
channels_align->set_padding (0, 12, 18, 0);
|
||||
channels_vbox->pack_start (*channels_label, false, false, 0);
|
||||
channels_vbox->pack_start (*channels_align, true, true, 0);
|
||||
advanced_paned->pack2(*channels_vbox, channel_selector_is_expandable(), false);
|
||||
|
||||
get_vbox()->pack_start (*file_notebook, false, false, 0);
|
||||
get_vbox()->pack_start (warning_widget, false, false, 0);
|
||||
get_vbox()->pack_start (progress_widget, false, false, 0);
|
||||
|
||||
advanced = Gtk::manage (new Gtk::Expander (_("Time span and channel options")));
|
||||
advanced->property_expanded().signal_changed().connect(
|
||||
sigc::mem_fun(*this, &ExportDialog::expanded_changed));
|
||||
advanced->add (*advanced_paned);
|
||||
|
||||
if (channel_selector_is_expandable()) {
|
||||
advanced_sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_VERTICAL);
|
||||
advanced_sizegroup->add_widget(*timespan_selector);
|
||||
advanced_sizegroup->add_widget(*channel_selector);
|
||||
}
|
||||
|
||||
get_vbox()->pack_start (*advanced, true, true);
|
||||
|
||||
Pango::AttrList bold;
|
||||
Pango::Attribute b = Pango::Attribute::create_attr_weight (Pango::WEIGHT_BOLD);
|
||||
bold.insert (b);
|
||||
|
||||
timespan_label->set_attributes (bold);
|
||||
channels_label->set_attributes (bold);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -211,6 +169,7 @@ ExportDialog::init_components ()
|
|||
preset_selector.reset (new ExportPresetSelector ());
|
||||
timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
|
||||
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
|
||||
soundcloud_selector.reset (new SoundcloudExportSelector ());
|
||||
file_notebook.reset (new ExportFileNotebook ());
|
||||
}
|
||||
|
||||
|
|
@ -300,11 +259,34 @@ ExportDialog::show_conflicting_files ()
|
|||
dialog.run();
|
||||
}
|
||||
|
||||
void
|
||||
ExportDialog::soundcloud_upload_progress(double total, double now, std::string title)
|
||||
{
|
||||
soundcloud_selector->do_progress_callback(total, now, title);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ExportDialog::do_export ()
|
||||
{
|
||||
try {
|
||||
profile_manager->prepare_for_export ();
|
||||
handler->upload_username = soundcloud_selector->username();
|
||||
handler->upload_password = soundcloud_selector->password();
|
||||
handler->upload_public = soundcloud_selector->upload_public();
|
||||
handler->upload_open = soundcloud_selector->upload_open();
|
||||
|
||||
handler->SoundcloudProgress.connect_same_thread(
|
||||
*this,
|
||||
boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3)
|
||||
);
|
||||
#if 0
|
||||
handler->SoundcloudProgress.connect(
|
||||
*this, invalidator (*this),
|
||||
boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3),
|
||||
gui_context()
|
||||
);
|
||||
#endif
|
||||
handler->do_export ();
|
||||
show_progress ();
|
||||
} catch(std::exception & e) {
|
||||
|
|
@ -418,6 +400,7 @@ ExportRangeDialog::init_components ()
|
|||
preset_selector.reset (new ExportPresetSelector ());
|
||||
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, range_id));
|
||||
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
|
||||
soundcloud_selector.reset (new SoundcloudExportSelector ());
|
||||
file_notebook.reset (new ExportFileNotebook ());
|
||||
}
|
||||
|
||||
|
|
@ -431,6 +414,7 @@ ExportSelectionDialog::init_components ()
|
|||
preset_selector.reset (new ExportPresetSelector ());
|
||||
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, X_("selection")));
|
||||
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
|
||||
soundcloud_selector.reset (new SoundcloudExportSelector ());
|
||||
file_notebook.reset (new ExportFileNotebook ());
|
||||
}
|
||||
|
||||
|
|
@ -444,8 +428,7 @@ void
|
|||
ExportRegionDialog::init_gui ()
|
||||
{
|
||||
ExportDialog::init_gui ();
|
||||
|
||||
channels_label->set_text (_("Source"));
|
||||
export_notebook.set_tab_label_text(*export_notebook.get_nth_page(2), _("Source"));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -456,6 +439,7 @@ ExportRegionDialog::init_components ()
|
|||
preset_selector.reset (new ExportPresetSelector ());
|
||||
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, loc_id));
|
||||
channel_selector.reset (new RegionExportChannelSelector (_session, profile_manager, region, track));
|
||||
soundcloud_selector.reset (new SoundcloudExportSelector ());
|
||||
file_notebook.reset (new ExportFileNotebook ());
|
||||
}
|
||||
|
||||
|
|
@ -471,5 +455,6 @@ StemExportDialog::init_components ()
|
|||
preset_selector.reset (new ExportPresetSelector ());
|
||||
timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
|
||||
channel_selector.reset (new TrackExportChannelSelector (_session, profile_manager));
|
||||
soundcloud_selector.reset (new SoundcloudExportSelector ());
|
||||
file_notebook.reset (new ExportFileNotebook ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "export_file_notebook.h"
|
||||
#include "export_preset_selector.h"
|
||||
#include "ardour_dialog.h"
|
||||
#include "soundcloud_export_selector.h"
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
|
|
@ -43,7 +44,8 @@ namespace ARDOUR {
|
|||
class ExportTimespanSelector;
|
||||
class ExportChannelSelector;
|
||||
|
||||
class ExportDialog : public ArdourDialog {
|
||||
class ExportDialog : public ArdourDialog, public PBD::ScopedConnectionList
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -75,26 +77,22 @@ class ExportDialog : public ArdourDialog {
|
|||
// Must initialize all the shared_ptrs below
|
||||
virtual void init_components ();
|
||||
|
||||
// Override if the channel selector should not be grown
|
||||
virtual bool channel_selector_is_expandable() { return true; }
|
||||
|
||||
boost::scoped_ptr<ExportPresetSelector> preset_selector;
|
||||
boost::scoped_ptr<ExportTimespanSelector> timespan_selector;
|
||||
boost::scoped_ptr<ExportChannelSelector> channel_selector;
|
||||
boost::scoped_ptr<ExportFileNotebook> file_notebook;
|
||||
boost::scoped_ptr<SoundcloudExportSelector> soundcloud_selector;
|
||||
|
||||
Gtk::VBox warning_widget;
|
||||
Gtk::VBox progress_widget;
|
||||
|
||||
Gtk::Label * timespan_label;
|
||||
Gtk::Label * channels_label;
|
||||
/*** GUI components ***/
|
||||
Gtk::Notebook export_notebook;
|
||||
|
||||
private:
|
||||
|
||||
void init ();
|
||||
|
||||
void expanded_changed();
|
||||
|
||||
void notify_errors (bool force = false);
|
||||
void close_dialog ();
|
||||
|
||||
|
|
@ -112,10 +110,7 @@ class ExportDialog : public ArdourDialog {
|
|||
PublicEditor & editor;
|
||||
StatusPtr status;
|
||||
|
||||
/*** GUI components ***/
|
||||
|
||||
Glib::RefPtr<Gtk::SizeGroup> advanced_sizegroup;
|
||||
Gtk::Expander * advanced;
|
||||
|
||||
/* Warning area */
|
||||
|
||||
|
|
@ -138,6 +133,8 @@ class ExportDialog : public ArdourDialog {
|
|||
|
||||
float previous_progress; // Needed for gtk bug workaround
|
||||
|
||||
void soundcloud_upload_progress(double total, double now, std::string title);
|
||||
|
||||
/* Buttons */
|
||||
|
||||
Gtk::Button * cancel_button;
|
||||
|
|
@ -170,9 +167,6 @@ class ExportRegionDialog : public ExportDialog
|
|||
public:
|
||||
ExportRegionDialog (PublicEditor & editor, ARDOUR::AudioRegion const & region, ARDOUR::AudioTrack & track);
|
||||
|
||||
protected:
|
||||
virtual bool channel_selector_is_expandable() { return false; }
|
||||
|
||||
private:
|
||||
void init_gui ();
|
||||
void init_components ();
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
|
|||
silence_end_checkbox (_("Add silence at end:")),
|
||||
silence_end_clock ("silence_end", true, "", true, false, true),
|
||||
|
||||
upload_checkbox(_("Upload to Soundcloud")),
|
||||
command_label(_("Command to run post-export\n(%f=full path & filename, %d=directory, %b=basename, %u=username, %p=password):")),
|
||||
|
||||
format_table (3, 4),
|
||||
compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT),
|
||||
quality_label (_("Quality"), Gtk::ALIGN_LEFT),
|
||||
|
|
@ -113,6 +116,10 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
|
|||
silence_table.attach (silence_end_checkbox, 1, 2, 2, 3);
|
||||
silence_table.attach (silence_end_clock, 2, 3, 2, 3);
|
||||
|
||||
get_vbox()->pack_start (upload_checkbox, false, false);
|
||||
get_vbox()->pack_start (command_label, false, false);
|
||||
get_vbox()->pack_start (command_entry, false, false);
|
||||
|
||||
/* Format table */
|
||||
|
||||
init_format_table();
|
||||
|
|
@ -142,6 +149,8 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
|
|||
|
||||
with_cue.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_cue));
|
||||
with_toc.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_toc));
|
||||
upload_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_upload));
|
||||
command_entry.signal_changed().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_command));
|
||||
|
||||
cue_toc_vbox.pack_start (with_cue, false, false);
|
||||
cue_toc_vbox.pack_start (with_toc, false, false);
|
||||
|
|
@ -296,6 +305,8 @@ ExportFormatDialog::load_state (FormatPtr spec)
|
|||
}
|
||||
|
||||
tag_checkbox.set_active (spec->tag());
|
||||
upload_checkbox.set_active (spec->upload());
|
||||
command_entry.set_text (spec->command());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -717,6 +728,18 @@ ExportFormatDialog::update_with_toc ()
|
|||
manager.select_with_toc (with_toc.get_active());
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatDialog::update_upload ()
|
||||
{
|
||||
manager.select_upload (upload_checkbox.get_active());
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatDialog::update_command ()
|
||||
{
|
||||
manager.set_command (command_entry.get_text());
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatDialog::update_description()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -179,6 +179,12 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
|
|||
Gtk::CheckButton silence_end_checkbox;
|
||||
AudioClock silence_end_clock;
|
||||
|
||||
/* Upload */
|
||||
|
||||
Gtk::CheckButton upload_checkbox;
|
||||
Gtk::Label command_label;
|
||||
Gtk::Entry command_entry;
|
||||
|
||||
/* Format table */
|
||||
|
||||
struct CompatibilityCols : public Gtk::TreeModelColumnRecord
|
||||
|
|
@ -311,6 +317,8 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
|
|||
|
||||
void update_with_toc ();
|
||||
void update_with_cue ();
|
||||
void update_upload ();
|
||||
void update_command ();
|
||||
|
||||
Gtk::TreeView sample_format_view;
|
||||
Gtk::TreeView dither_type_view;
|
||||
|
|
|
|||
|
|
@ -1,209 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2006 Paul Davis
|
||||
Author: Andre Raue
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/sndfile_helpers.h"
|
||||
|
||||
#include "ardour_ui.h"
|
||||
#include "export_range_markers_dialog.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace Gtk;
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace std;
|
||||
|
||||
ExportRangeMarkersDialog::ExportRangeMarkersDialog (PublicEditor& editor)
|
||||
: ExportDialog(editor)
|
||||
{
|
||||
set_title (_("Export Ranges"));
|
||||
file_frame.set_label (_("Export to Directory"));
|
||||
|
||||
do_not_allow_export_cd_markers();
|
||||
|
||||
total_duration = 0;
|
||||
current_range_marker_index = 0;
|
||||
}
|
||||
|
||||
Gtk::FileChooserAction
|
||||
ExportRangeMarkersDialog::browse_action () const
|
||||
{
|
||||
return Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER;
|
||||
}
|
||||
|
||||
void
|
||||
ExportRangeMarkersDialog::export_data ()
|
||||
{
|
||||
getSession().locations()->apply(*this, &ExportRangeMarkersDialog::process_range_markers_export);
|
||||
}
|
||||
|
||||
void
|
||||
ExportRangeMarkersDialog::process_range_markers_export(Locations::LocationList& locations)
|
||||
{
|
||||
Locations::LocationList::iterator locationIter;
|
||||
current_range_marker_index = 0;
|
||||
init_progress_computing(locations);
|
||||
|
||||
for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) {
|
||||
Location *currentLocation = (*locationIter);
|
||||
|
||||
if(currentLocation->is_range_marker()){
|
||||
// init filename
|
||||
string filepath = get_target_filepath(
|
||||
get_selected_file_name(),
|
||||
currentLocation->name(),
|
||||
get_selected_header_format());
|
||||
|
||||
initSpec(filepath);
|
||||
|
||||
spec.start_frame = currentLocation->start();
|
||||
spec.end_frame = currentLocation->end();
|
||||
|
||||
if (getSession().start_export(spec)){
|
||||
// if export fails
|
||||
return;
|
||||
}
|
||||
|
||||
// wait until export of this range finished
|
||||
gtk_main_iteration();
|
||||
|
||||
while (spec.running){
|
||||
if(gtk_events_pending()){
|
||||
gtk_main_iteration();
|
||||
}else {
|
||||
Glib::usleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
current_range_marker_index++;
|
||||
|
||||
getSession().stop_export (spec);
|
||||
}
|
||||
}
|
||||
|
||||
spec.running = false;
|
||||
}
|
||||
|
||||
|
||||
string
|
||||
ExportRangeMarkersDialog::get_target_filepath(string path, string filename, string postfix)
|
||||
{
|
||||
string target_path = path;
|
||||
if ((target_path.find_last_of ('/')) != string::npos) {
|
||||
target_path += '/';
|
||||
}
|
||||
|
||||
string target_filepath = target_path + filename + postfix;
|
||||
struct stat statbuf;
|
||||
|
||||
for(int counter=1; (stat (target_filepath.c_str(), &statbuf) == 0); counter++){
|
||||
// while file exists
|
||||
ostringstream scounter;
|
||||
scounter.flush();
|
||||
scounter << counter;
|
||||
|
||||
target_filepath =
|
||||
target_path + filename + "_" + scounter.str() + postfix;
|
||||
}
|
||||
|
||||
return target_filepath;
|
||||
}
|
||||
|
||||
bool
|
||||
ExportRangeMarkersDialog::is_filepath_valid(string &filepath)
|
||||
{
|
||||
// sanity check file name first
|
||||
struct stat statbuf;
|
||||
|
||||
if (filepath.empty()) {
|
||||
// warning dialog
|
||||
string txt = _("Please enter a valid target directory.");
|
||||
MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
|
||||
msg.run();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( (stat (filepath.c_str(), &statbuf) != 0) ||
|
||||
(!S_ISDIR (statbuf.st_mode)) ) {
|
||||
string txt = _("Please select an existing target directory. Files are not allowed!");
|
||||
MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
|
||||
msg.run();
|
||||
return false;
|
||||
}
|
||||
|
||||
// directory needs to exist and be writable
|
||||
string dirpath = Glib::path_get_dirname (filepath);
|
||||
if (!exists_and_writable (dirpath)) {
|
||||
string txt = _("Cannot write file in: ") + dirpath;
|
||||
MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
|
||||
msg.run();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ExportRangeMarkersDialog::init_progress_computing(Locations::LocationList& locations)
|
||||
{
|
||||
// flush vector
|
||||
range_markers_durations_aggregated.resize(0);
|
||||
|
||||
framecnt_t duration_before_current_location = 0;
|
||||
Locations::LocationList::iterator locationIter;
|
||||
|
||||
for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) {
|
||||
Location *currentLocation = (*locationIter);
|
||||
|
||||
if(currentLocation->is_range_marker()){
|
||||
range_markers_durations_aggregated.push_back (duration_before_current_location);
|
||||
|
||||
framecnt_t duration = currentLocation->end() - currentLocation->start();
|
||||
|
||||
range_markers_durations.push_back (duration);
|
||||
duration_before_current_location += duration;
|
||||
}
|
||||
}
|
||||
|
||||
total_duration = duration_before_current_location;
|
||||
}
|
||||
|
||||
|
||||
gint
|
||||
ExportRangeMarkersDialog::progress_timeout ()
|
||||
{
|
||||
double progress = 0.0;
|
||||
|
||||
if (current_range_marker_index >= range_markers_durations.size()){
|
||||
progress = 1.0;
|
||||
} else{
|
||||
progress = ((double) range_markers_durations_aggregated[current_range_marker_index] +
|
||||
(spec.progress * (double) range_markers_durations[current_range_marker_index])) /
|
||||
(double) total_duration;
|
||||
}
|
||||
|
||||
set_progress_fraction( progress );
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2006 Andre Raue
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __export_range_markers_dialog_h__
|
||||
#define __export_range_markers_dialog_h__
|
||||
|
||||
#include "ardour/location.h"
|
||||
|
||||
#include "export_dialog.h"
|
||||
|
||||
|
||||
class ExportRangeMarkersDialog : public ExportDialog
|
||||
{
|
||||
public:
|
||||
ExportRangeMarkersDialog (PublicEditor&);
|
||||
|
||||
Gtk::FileChooserAction browse_action() const;
|
||||
|
||||
protected:
|
||||
virtual bool is_filepath_valid(string &filepath);
|
||||
|
||||
void export_data();
|
||||
|
||||
bool wants_dir() { return true; }
|
||||
|
||||
private:
|
||||
// keeps the duration of all range_markers before the current
|
||||
vector<nframes_t> range_markers_durations_aggregated;
|
||||
vector<nframes_t> range_markers_durations;
|
||||
// duration of all range markers
|
||||
nframes_t total_duration;
|
||||
// index of range marker, that get's exported right now
|
||||
unsigned int current_range_marker_index;
|
||||
|
||||
// sets value of progress bar
|
||||
virtual gint progress_timeout ();
|
||||
|
||||
// initializes range_markers_durations_aggregated, range_markers_durations
|
||||
// and total_duration
|
||||
void init_progress_computing(ARDOUR::Locations::LocationList& locations);
|
||||
|
||||
// searches for a filename like "<filename><nr>.<postfix>" in path, that
|
||||
// does not exist
|
||||
string get_target_filepath(string path, string filename, string postfix);
|
||||
|
||||
void process_range_markers_export(ARDOUR::Locations::LocationList&);
|
||||
};
|
||||
|
||||
|
||||
#endif // __export_range_markers_dialog_h__
|
||||
|
|
@ -105,6 +105,9 @@ ExportTimespanSelector::ExportTimespanSelector (ARDOUR::Session * session, Profi
|
|||
/* Range view */
|
||||
|
||||
range_list = Gtk::ListStore::create (range_cols);
|
||||
// order by location start times
|
||||
range_list->set_sort_column(range_cols.location, Gtk::SORT_ASCENDING);
|
||||
range_list->set_sort_func(range_cols.location, sigc::mem_fun(*this, &ExportTimespanSelector::location_sorter));
|
||||
range_view.set_model (range_list);
|
||||
range_view.set_headers_visible (true);
|
||||
}
|
||||
|
|
@ -114,6 +117,22 @@ ExportTimespanSelector::~ExportTimespanSelector ()
|
|||
|
||||
}
|
||||
|
||||
int
|
||||
ExportTimespanSelector::location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b)
|
||||
{
|
||||
Location *l1 = (*a)[range_cols.location];
|
||||
Location *l2 = (*b)[range_cols.location];
|
||||
const Location *ls = _session->locations()->session_range_location();
|
||||
|
||||
// always sort session range first
|
||||
if (l1 == ls)
|
||||
return -1;
|
||||
if (l2 == ls)
|
||||
return +1;
|
||||
|
||||
return l1->start() - l2->start();
|
||||
}
|
||||
|
||||
void
|
||||
ExportTimespanSelector::add_range_to_selection (ARDOUR::Location const * loc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
|
|||
void update_range_name (std::string const & path, std::string const & new_text);
|
||||
|
||||
void set_selection_state_of_all_timespans (bool);
|
||||
int location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b);
|
||||
|
||||
/*** GUI components ***/
|
||||
|
||||
|
|
@ -132,7 +133,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
|
|||
Gtk::ScrolledWindow range_scroller;
|
||||
};
|
||||
|
||||
/// Allows seleting multiple timespans
|
||||
/// Allows selecting multiple timespans
|
||||
class ExportTimespanSelectorMultiple : public ExportTimespanSelector
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
BIN
gtk2_ardour/icons/soundcloud.png
Normal file
BIN
gtk2_ardour/icons/soundcloud.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
110
gtk2_ardour/soundcloud_export_selector.cc
Normal file
110
gtk2_ardour/soundcloud_export_selector.cc
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* soundcloud_export_selector.cpp ***************************************************
|
||||
|
||||
Adapted for Ardour by Ben Loftis, March 2012
|
||||
|
||||
Licence GPL:
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
*************************************************************************************/
|
||||
#include "ardour/soundcloud_upload.h"
|
||||
#include "soundcloud_export_selector.h"
|
||||
|
||||
#include <pbd/error.h>
|
||||
#include "pbd/openuri.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <iostream>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
#include "ardour/session_metadata.h"
|
||||
#include "utils.h"
|
||||
|
||||
SoundcloudExportSelector::SoundcloudExportSelector() :
|
||||
sc_table (4, 3),
|
||||
soundcloud_public_checkbox (_("Make file(s) public")),
|
||||
soundcloud_username_label (_("User Email"), 1.0, 0.5),
|
||||
soundcloud_password_label (_("Password"), 1.0, 0.5),
|
||||
soundcloud_open_checkbox (_("Open uploaded files in browser")),
|
||||
progress_bar()
|
||||
{
|
||||
|
||||
|
||||
soundcloud_public_checkbox.set_name ("ExportCheckbox");
|
||||
soundcloud_username_label.set_name ("ExportFormatLabel");
|
||||
soundcloud_username_entry.set_name ("ExportFormatDisplay");
|
||||
soundcloud_password_label.set_name ("ExportFormatLabel");
|
||||
soundcloud_password_entry.set_name ("ExportFormatDisplay");
|
||||
|
||||
soundcloud_username_entry.set_text (ARDOUR::SessionMetadata::Metadata()->user_email());
|
||||
soundcloud_password_entry.set_visibility(false);
|
||||
|
||||
Gtk::Frame *sc_frame = manage(new Gtk::Frame);
|
||||
sc_frame->set_border_width(4);
|
||||
sc_frame->set_shadow_type(Gtk::SHADOW_ETCHED_OUT);
|
||||
sc_frame->set_name("soundcloud_export_box");
|
||||
pack_start(*sc_frame, false, false);
|
||||
|
||||
sc_table.set_border_width(4);
|
||||
sc_table.set_col_spacings (5);
|
||||
sc_table.set_row_spacings (5);
|
||||
sc_frame->add (sc_table);
|
||||
|
||||
// sc_table.attach ( *( manage (new EventBox (::get_icon (X_("soundcloud"))))) , 0, 1, 0, 1);
|
||||
sc_table.attach ( *(Gtk::manage (new Gtk::Image (get_icon (X_("soundcloud"))))) , 0, 1, 0, 2);
|
||||
|
||||
sc_table.attach (soundcloud_public_checkbox, 2, 3, 1, 2);
|
||||
sc_table.attach (soundcloud_username_label, 0, 1, 3, 4);
|
||||
sc_table.attach (soundcloud_username_entry, 1, 3, 3, 4);
|
||||
sc_table.attach (soundcloud_password_label, 0, 1, 5, 6);
|
||||
sc_table.attach (soundcloud_password_entry, 1, 3, 5, 6);
|
||||
sc_table.attach (soundcloud_open_checkbox, 2, 3, 7, 8);
|
||||
|
||||
pack_end(progress_bar, false, false);
|
||||
sc_frame->show_all();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
SoundcloudExportSelector::do_progress_callback(double ultotal, double ulnow, const std::string &filename)
|
||||
{
|
||||
std::cerr << "SoundcloudExportSelector::do_progress_callback(" << ultotal << ", " << ulnow << ", " << filename << ")..." << std::endl;
|
||||
if (soundcloud_cancel) {
|
||||
progress_bar.set_fraction (0);
|
||||
// cancel_button.set_label ("");
|
||||
return -1;
|
||||
}
|
||||
|
||||
double fraction = 0.0;
|
||||
if (ultotal != 0) {
|
||||
fraction = ulnow / ultotal;
|
||||
}
|
||||
|
||||
progress_bar.set_fraction ( fraction );
|
||||
|
||||
std::string prog;
|
||||
prog = string_compose (_("%1: %2 of %3 bytes uploaded"), filename, ulnow, ultotal);
|
||||
progress_bar.set_text( prog );
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
41
gtk2_ardour/soundcloud_export_selector.h
Normal file
41
gtk2_ardour/soundcloud_export_selector.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*soundcloud_export_selector.h***********************************************
|
||||
|
||||
Adapted for Ardour by Ben Loftis, March 2012
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <gtkmm.h>
|
||||
#include <gtkmm/progressbar.h>
|
||||
|
||||
class SoundcloudExportSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
|
||||
{
|
||||
public:
|
||||
SoundcloudExportSelector ();
|
||||
int do_progress_callback (double ultotal, double ulnow, const std::string &filename);
|
||||
std::string username () { return soundcloud_username_entry.get_text (); }
|
||||
std::string password () { return soundcloud_password_entry.get_text (); }
|
||||
bool upload_public () { return soundcloud_public_checkbox.get_active (); }
|
||||
bool upload_open () { return soundcloud_open_checkbox.get_active (); }
|
||||
void cancel () { soundcloud_cancel = true; }
|
||||
|
||||
private:
|
||||
Gtk::Table sc_table;
|
||||
Gtk::CheckButton soundcloud_public_checkbox;
|
||||
Gtk::Label soundcloud_username_label;
|
||||
Gtk::Entry soundcloud_username_entry;
|
||||
Gtk::Label soundcloud_password_label;
|
||||
Gtk::Entry soundcloud_password_entry;
|
||||
Gtk::CheckButton soundcloud_open_checkbox;
|
||||
bool soundcloud_cancel;
|
||||
Gtk::ProgressBar progress_bar;
|
||||
|
||||
};
|
||||
|
||||
|
|
@ -204,6 +204,7 @@ gtk2_ardour_sources = [
|
|||
'session_option_editor.cc',
|
||||
'sfdb_ui.cc',
|
||||
'shuttle_control.cc',
|
||||
'soundcloud_export_selector.cc',
|
||||
'splash.cc',
|
||||
'speaker_dialog.cc',
|
||||
'startup.cc',
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ class LIBARDOUR_API ExportFormatManager : public PBD::ScopedConnectionList
|
|||
|
||||
void select_with_cue (bool);
|
||||
void select_with_toc (bool);
|
||||
void select_upload (bool);
|
||||
void set_command (std::string);
|
||||
void select_src_quality (ExportFormatBase::SRCQuality value);
|
||||
void select_trim_beginning (bool value);
|
||||
void select_silence_beginning (AnyTime const & time);
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
|
|||
void set_tag (bool tag_it) { _tag = tag_it; }
|
||||
void set_with_cue (bool yn) { _with_cue = yn; }
|
||||
void set_with_toc (bool yn) { _with_toc = yn; }
|
||||
void set_upload (bool yn) { _upload = yn; }
|
||||
void set_command (std::string command) { _command = command; }
|
||||
|
||||
void set_silence_beginning (AnyTime const & value) { _silence_beginning = value; }
|
||||
void set_silence_end (AnyTime const & value) { _silence_end = value; }
|
||||
|
|
@ -125,6 +127,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
|
|||
float normalize_target () const { return _normalize_target; }
|
||||
bool with_toc() const { return _with_toc; }
|
||||
bool with_cue() const { return _with_cue; }
|
||||
bool upload() const { return _upload; }
|
||||
std::string command() const { return _command; }
|
||||
|
||||
bool tag () const { return _tag && supports_tagging; }
|
||||
|
||||
|
|
@ -174,6 +178,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
|
|||
float _normalize_target;
|
||||
bool _with_toc;
|
||||
bool _with_cue;
|
||||
bool _upload;
|
||||
std::string _command;
|
||||
|
||||
/* serialization helpers */
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "ardour/session.h"
|
||||
#include "ardour/libardour_visibility.h"
|
||||
#include "ardour/types.h"
|
||||
#include "pbd/signals.h"
|
||||
|
||||
namespace AudioGrapher {
|
||||
class BroadcastInfo;
|
||||
|
|
@ -68,7 +69,7 @@ class LIBARDOUR_API ExportElementFactory
|
|||
Session & session;
|
||||
};
|
||||
|
||||
class LIBARDOUR_API ExportHandler : public ExportElementFactory
|
||||
class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::trackable
|
||||
{
|
||||
public:
|
||||
struct FileSpec {
|
||||
|
|
@ -95,6 +96,8 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory
|
|||
friend boost::shared_ptr<ExportHandler> Session::get_export_handler();
|
||||
ExportHandler (Session & session);
|
||||
|
||||
void command_output(std::string output, size_t size);
|
||||
|
||||
public:
|
||||
~ExportHandler ();
|
||||
|
||||
|
|
@ -105,6 +108,17 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory
|
|||
|
||||
std::string get_cd_marker_filename(std::string filename, CDMarkerFormat format);
|
||||
|
||||
/** signal emitted when soundcloud export reports progress updates during upload.
|
||||
* The parameters are total and current bytes downloaded, and the current filename
|
||||
*/
|
||||
PBD::Signal3<void, double, double, std::string> SoundcloudProgress;
|
||||
|
||||
/* upload credentials & preferences */
|
||||
std::string upload_username;
|
||||
std::string upload_password;
|
||||
bool upload_public;
|
||||
bool upload_open;
|
||||
|
||||
private:
|
||||
|
||||
void handle_duplicate_format_extensions();
|
||||
|
|
|
|||
55
libs/ardour/ardour/soundcloud_upload.h
Normal file
55
libs/ardour/ardour/soundcloud_upload.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* soundcloud_upload.h ******************************************************
|
||||
|
||||
Adapted for Ardour by Ben Loftis, March 2012
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __ardour_soundcloud_upload_h__
|
||||
#define __ardour_soundcloud_upload_h__
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "curl/curl.h"
|
||||
#include "ardour/session_handle.h"
|
||||
#include "ardour/export_handler.h"
|
||||
#include "pbd/signals.h"
|
||||
|
||||
//--- struct to store XML file
|
||||
struct MemoryStruct {
|
||||
char *memory;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
|
||||
class SoundcloudUploader
|
||||
{
|
||||
public:
|
||||
SoundcloudUploader();
|
||||
~SoundcloudUploader();
|
||||
|
||||
std::string Get_Auth_Token(std::string username, std::string password);
|
||||
std::string Upload (std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller);
|
||||
static int progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void setcUrlOptions();
|
||||
|
||||
CURL *curl_handle;
|
||||
CURLM *multi_handle;
|
||||
char errorBuffer[CURL_ERROR_SIZE]; // storage for cUrl error message
|
||||
|
||||
std::string title;
|
||||
ARDOUR::ExportHandler *caller;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __ardour_soundcloud_upload_h__ */
|
||||
|
|
@ -32,6 +32,7 @@ class LIBARDOUR_API SystemExec
|
|||
public:
|
||||
SystemExec (std::string c, std::string a = "");
|
||||
SystemExec (std::string c, char ** a);
|
||||
SystemExec (std::string c, const std::map<char, std::string> subs);
|
||||
~SystemExec ();
|
||||
|
||||
int start (int stderr_mode = 1) {
|
||||
|
|
|
|||
|
|
@ -293,6 +293,20 @@ ExportFormatManager::select_with_toc (bool value)
|
|||
check_for_description_change ();
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_upload (bool value)
|
||||
{
|
||||
current_selection->set_upload (value);
|
||||
check_for_description_change ();
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::set_command (std::string command)
|
||||
{
|
||||
current_selection->set_command (command);
|
||||
check_for_description_change ();
|
||||
}
|
||||
|
||||
void
|
||||
ExportFormatManager::select_trim_beginning (bool value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -170,6 +170,8 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s)
|
|||
, _normalize_target (1.0)
|
||||
, _with_toc (false)
|
||||
, _with_cue (false)
|
||||
, _upload (false)
|
||||
, _command ("")
|
||||
{
|
||||
format_ids.insert (F_None);
|
||||
endiannesses.insert (E_FileDefault);
|
||||
|
|
@ -244,6 +246,8 @@ ExportFormatSpecification::get_state ()
|
|||
root->add_property ("id", _id.to_s());
|
||||
root->add_property ("with-cue", _with_cue ? "true" : "false");
|
||||
root->add_property ("with-toc", _with_toc ? "true" : "false");
|
||||
root->add_property ("upload", _upload ? "true" : "false");
|
||||
root->add_property ("command", _command);
|
||||
|
||||
node = root->add_child ("Encoding");
|
||||
node->add_property ("id", enum_2_string (format_id()));
|
||||
|
|
@ -321,6 +325,18 @@ ExportFormatSpecification::set_state (const XMLNode & root)
|
|||
_with_toc = false;
|
||||
}
|
||||
|
||||
if ((prop = root.property ("upload"))) {
|
||||
_upload = string_is_affirmative (prop->value());
|
||||
} else {
|
||||
_upload = false;
|
||||
}
|
||||
|
||||
if ((prop = root.property ("command"))) {
|
||||
_command = prop->value();
|
||||
} else {
|
||||
_command = "";
|
||||
}
|
||||
|
||||
/* Encoding and SRC */
|
||||
|
||||
if ((child = root.child ("Encoding"))) {
|
||||
|
|
@ -590,6 +606,14 @@ ExportFormatSpecification::description (bool include_name)
|
|||
components.push_back ("CUE");
|
||||
}
|
||||
|
||||
if (_upload) {
|
||||
components.push_back ("Upload");
|
||||
}
|
||||
|
||||
if (!_command.empty()) {
|
||||
components.push_back ("+");
|
||||
}
|
||||
|
||||
string desc;
|
||||
if (include_name) {
|
||||
desc = _name + ": ";
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@
|
|||
#include "ardour/export_status.h"
|
||||
#include "ardour/export_format_specification.h"
|
||||
#include "ardour/export_filename.h"
|
||||
#include "ardour/soundcloud_upload.h"
|
||||
#include "ardour/system_exec.h"
|
||||
#include "pbd/openuri.h"
|
||||
#include "pbd/basename.h"
|
||||
#include "ardour/session_metadata.h"
|
||||
|
||||
#include "i18n.h"
|
||||
|
|
@ -277,6 +281,13 @@ ExportHandler::process_normalize ()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::command_output(std::string output, size_t size)
|
||||
{
|
||||
std::cerr << "command: " << size << ", " << output << std::endl;
|
||||
info << output << endmsg;
|
||||
}
|
||||
|
||||
void
|
||||
ExportHandler::finish_timespan ()
|
||||
{
|
||||
|
|
@ -297,13 +308,77 @@ ExportHandler::finish_timespan ()
|
|||
AudiofileTagger::tag_file(filename, *SessionMetadata::Metadata());
|
||||
}
|
||||
|
||||
if (!fmt->command().empty()) {
|
||||
|
||||
#if 0 // would be nicer with C++11 initialiser...
|
||||
std::map<char, std::string> subs {
|
||||
{ 'f', filename },
|
||||
{ 'd', Glib::path_get_dirname(filename) },
|
||||
{ 'b', PBD::basename_nosuffix(filename) },
|
||||
{ 'u', upload_username },
|
||||
{ 'p', upload_password}
|
||||
};
|
||||
#endif
|
||||
|
||||
PBD::ScopedConnection command_connection;
|
||||
std::map<char, std::string> subs;
|
||||
subs.insert (std::pair<char, std::string> ('f', filename));
|
||||
subs.insert (std::pair<char, std::string> ('d', Glib::path_get_dirname(filename)));
|
||||
subs.insert (std::pair<char, std::string> ('b', PBD::basename_nosuffix(filename)));
|
||||
subs.insert (std::pair<char, std::string> ('u', upload_username));
|
||||
subs.insert (std::pair<char, std::string> ('p', upload_password));
|
||||
|
||||
|
||||
std::cerr << "running command: " << fmt->command() << "..." << std::endl;
|
||||
ARDOUR::SystemExec *se = new ARDOUR::SystemExec(fmt->command(), subs);
|
||||
se->ReadStdout.connect_same_thread(command_connection, boost::bind(&ExportHandler::command_output, this, _1, _2));
|
||||
if (se->start (2) == 0) {
|
||||
// successfully started
|
||||
std::cerr << "started!" << std::endl;
|
||||
while (se->is_running ()) {
|
||||
// wait for system exec to terminate
|
||||
// std::cerr << "waiting..." << std::endl;
|
||||
usleep (1000);
|
||||
}
|
||||
}
|
||||
std::cerr << "done! deleting..." << std::endl;
|
||||
delete (se);
|
||||
}
|
||||
|
||||
if (fmt->upload()) {
|
||||
SoundcloudUploader *soundcloud_uploader = new SoundcloudUploader;
|
||||
std::string token = soundcloud_uploader->Get_Auth_Token(upload_username, upload_password);
|
||||
std::cerr
|
||||
<< "uploading "
|
||||
<< filename << std::endl
|
||||
<< "username = " << upload_username
|
||||
<< ", password = " << upload_password
|
||||
<< " - token = " << token << " ..."
|
||||
<< std::endl;
|
||||
std::string path = soundcloud_uploader->Upload (
|
||||
filename,
|
||||
PBD::basename_nosuffix(filename), // title
|
||||
token,
|
||||
upload_public,
|
||||
this);
|
||||
|
||||
if (path.length() != 0) {
|
||||
if (upload_open) {
|
||||
std::cerr << "opening " << path << " ..." << std::endl;
|
||||
open_uri(path.c_str()); // open the soundcloud website to the new file
|
||||
}
|
||||
} else {
|
||||
error << _("upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
|
||||
}
|
||||
delete soundcloud_uploader;
|
||||
}
|
||||
config_map.erase (config_map.begin());
|
||||
}
|
||||
|
||||
start_timespan ();
|
||||
}
|
||||
|
||||
/*** CD Marker sutff ***/
|
||||
/*** CD Marker stuff ***/
|
||||
|
||||
struct LocationSortByStart {
|
||||
bool operator() (Location *a, Location *b) {
|
||||
|
|
|
|||
349
libs/ardour/soundcloud_upload.cc
Normal file
349
libs/ardour/soundcloud_upload.cc
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/* soundcloud_export.cpp **********************************************************************
|
||||
|
||||
Adapted for Ardour by Ben Loftis, March 2012
|
||||
|
||||
Licence GPL:
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
*************************************************************************************/
|
||||
#include "ardour/soundcloud_upload.h"
|
||||
|
||||
#include "pbd/xml++.h"
|
||||
#include <pbd/error.h>
|
||||
//#include "pbd/filesystem.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <iostream>
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace PBD;
|
||||
|
||||
// static const std::string base_url = "http://api.soundcloud.com/tracks/13158665?client_id=";
|
||||
|
||||
size_t
|
||||
WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
|
||||
{
|
||||
register int realsize = (int)(size * nmemb);
|
||||
struct MemoryStruct *mem = (struct MemoryStruct *)data;
|
||||
|
||||
mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
|
||||
|
||||
if (mem->memory) {
|
||||
memcpy(&(mem->memory[mem->size]), ptr, realsize);
|
||||
mem->size += realsize;
|
||||
mem->memory[mem->size] = 0;
|
||||
}
|
||||
return realsize;
|
||||
}
|
||||
|
||||
SoundcloudUploader::SoundcloudUploader()
|
||||
{
|
||||
curl_handle = curl_easy_init();
|
||||
multi_handle = curl_multi_init();
|
||||
}
|
||||
|
||||
std::string
|
||||
SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
|
||||
{
|
||||
struct MemoryStruct xml_page;
|
||||
xml_page.memory = NULL;
|
||||
xml_page.size = 0;
|
||||
|
||||
setcUrlOptions();
|
||||
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
|
||||
|
||||
struct curl_httppost *formpost=NULL;
|
||||
struct curl_httppost *lastptr=NULL;
|
||||
|
||||
/* Fill in the filename field */
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "client_id",
|
||||
CURLFORM_COPYCONTENTS, "e7ac891eef866f139773cf8102b7a719",
|
||||
CURLFORM_END);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "client_secret",
|
||||
CURLFORM_COPYCONTENTS, "d78f34d19f09d26731801a0cb0f382c4",
|
||||
CURLFORM_END);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "grant_type",
|
||||
CURLFORM_COPYCONTENTS, "password",
|
||||
CURLFORM_END);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "username",
|
||||
CURLFORM_COPYCONTENTS, username.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "password",
|
||||
CURLFORM_COPYCONTENTS, password.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
struct curl_slist *headerlist=NULL;
|
||||
headerlist = curl_slist_append(headerlist, "Expect:");
|
||||
headerlist = curl_slist_append(headerlist, "Accept: application/xml");
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
|
||||
|
||||
/* what URL that receives this POST */
|
||||
std::string url = "https://api.soundcloud.com/oauth2/token";
|
||||
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
|
||||
|
||||
// curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
|
||||
|
||||
// perform online request
|
||||
CURLcode res = curl_easy_perform(curl_handle);
|
||||
if( res != 0 ) {
|
||||
std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
if(xml_page.memory){
|
||||
//cheesy way to parse the json return value. find access_token, then advance 3 quotes
|
||||
|
||||
if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
|
||||
error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string token = strtok( xml_page.memory, "access_token" );
|
||||
token = strtok( NULL, "\"" );
|
||||
token = strtok( NULL, "\"" );
|
||||
token = strtok( NULL, "\"" );
|
||||
|
||||
free( xml_page.memory );
|
||||
return token;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int
|
||||
SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
{
|
||||
SoundcloudUploader *scu = (SoundcloudUploader *) caller;
|
||||
std::cerr << scu->title << ": uploaded " << ulnow << " of " << ultotal << std::endl;
|
||||
scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller)
|
||||
{
|
||||
int still_running;
|
||||
|
||||
struct MemoryStruct xml_page;
|
||||
xml_page.memory = NULL;
|
||||
xml_page.size = 0;
|
||||
|
||||
setcUrlOptions();
|
||||
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
|
||||
|
||||
struct curl_httppost *formpost=NULL;
|
||||
struct curl_httppost *lastptr=NULL;
|
||||
|
||||
/* Fill in the file upload field. This makes libcurl load data from
|
||||
the given file name when curl_easy_perform() is called. */
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "track[asset_data]",
|
||||
CURLFORM_FILE, file_path.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
/* Fill in the filename field */
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "oauth_token",
|
||||
CURLFORM_COPYCONTENTS, token.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "track[title]",
|
||||
CURLFORM_COPYCONTENTS, title.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
curl_formadd(&formpost,
|
||||
&lastptr,
|
||||
CURLFORM_COPYNAME, "track[sharing]",
|
||||
CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
|
||||
CURLFORM_END);
|
||||
|
||||
/* initalize custom header list (stating that Expect: 100-continue is not
|
||||
wanted */
|
||||
struct curl_slist *headerlist=NULL;
|
||||
static const char buf[] = "Expect:";
|
||||
headerlist = curl_slist_append(headerlist, buf);
|
||||
|
||||
|
||||
if (curl_handle && multi_handle) {
|
||||
|
||||
/* what URL that receives this POST */
|
||||
std::string url = "https://api.soundcloud.com/tracks";
|
||||
curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
|
||||
// curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
|
||||
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
|
||||
|
||||
this->title = title; // save title to show in progress bar
|
||||
this->caller = caller;
|
||||
|
||||
curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
|
||||
curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
|
||||
curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
|
||||
|
||||
curl_multi_add_handle(multi_handle, curl_handle);
|
||||
|
||||
curl_multi_perform(multi_handle, &still_running);
|
||||
|
||||
|
||||
while(still_running) {
|
||||
struct timeval timeout;
|
||||
int rc; /* select() return code */
|
||||
|
||||
fd_set fdread;
|
||||
fd_set fdwrite;
|
||||
fd_set fdexcep;
|
||||
int maxfd = -1;
|
||||
|
||||
long curl_timeo = -1;
|
||||
|
||||
FD_ZERO(&fdread);
|
||||
FD_ZERO(&fdwrite);
|
||||
FD_ZERO(&fdexcep);
|
||||
|
||||
/* set a suitable timeout to play around with */
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
curl_multi_timeout(multi_handle, &curl_timeo);
|
||||
if(curl_timeo >= 0) {
|
||||
timeout.tv_sec = curl_timeo / 1000;
|
||||
if(timeout.tv_sec > 1)
|
||||
timeout.tv_sec = 1;
|
||||
else
|
||||
timeout.tv_usec = (curl_timeo % 1000) * 1000;
|
||||
}
|
||||
|
||||
/* get file descriptors from the transfers */
|
||||
curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
|
||||
|
||||
/* In a real-world program you OF COURSE check the return code of the
|
||||
function calls. On success, the value of maxfd is guaranteed to be
|
||||
greater or equal than -1. We call select(maxfd + 1, ...), specially in
|
||||
case of (maxfd == -1), we call select(0, ...), which is basically equal
|
||||
to sleep. */
|
||||
|
||||
rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
|
||||
|
||||
switch(rc) {
|
||||
case -1:
|
||||
/* select error */
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
/* timeout or readable/writable sockets */
|
||||
curl_multi_perform(multi_handle, &still_running);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* then cleanup the formpost chain */
|
||||
curl_formfree(formpost);
|
||||
|
||||
/* free slist */
|
||||
curl_slist_free_all (headerlist);
|
||||
}
|
||||
|
||||
curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
|
||||
|
||||
if(xml_page.memory){
|
||||
|
||||
std::cout << xml_page.memory << std::endl;
|
||||
|
||||
XMLTree doc;
|
||||
doc.read_buffer( xml_page.memory );
|
||||
XMLNode *root = doc.root();
|
||||
|
||||
if (!root) {
|
||||
std::cout << "no root XML node!" << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
XMLNode *url_node = root->child("permalink-url");
|
||||
if (!url_node) {
|
||||
std::cout << "no child node \"permalink-url\" found!" << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
XMLNode *text_node = url_node->child("text");
|
||||
if (!text_node) {
|
||||
std::cout << "no text node found!" << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
free( xml_page.memory );
|
||||
return text_node->content();
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
|
||||
SoundcloudUploader:: ~SoundcloudUploader()
|
||||
{
|
||||
curl_easy_cleanup(curl_handle);
|
||||
curl_multi_cleanup(multi_handle);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SoundcloudUploader::setcUrlOptions()
|
||||
{
|
||||
// basic init for curl
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
// some servers don't like requests that are made without a user-agent field, so we provide one
|
||||
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||
// setup curl error buffer
|
||||
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
|
||||
// Allow redirection
|
||||
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
|
||||
|
||||
// Allow connections to time out (without using signals)
|
||||
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
|
||||
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
}
|
||||
|
||||
|
|
@ -65,4 +65,14 @@ SystemExec::SystemExec (std::string c, std::string a)
|
|||
#endif
|
||||
}
|
||||
|
||||
SystemExec::SystemExec (std::string c, const std::map<char, std::string> subs)
|
||||
: PBD::SystemExec(c, subs)
|
||||
{
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
if (!_vfork_exec_wrapper) {
|
||||
_vfork_exec_wrapper = vfork_exec_wrapper_path();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SystemExec::~SystemExec() { }
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ libardour_sources = [
|
|||
'sndfile_helpers.cc',
|
||||
'sndfileimportable.cc',
|
||||
'sndfilesource.cc',
|
||||
'soundcloud_upload.cc',
|
||||
'source.cc',
|
||||
'source_factory.cc',
|
||||
'speakers.cc',
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@
|
|||
#include <string>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <map>
|
||||
|
||||
#ifdef NOPBD /* unit-test outside ardour */
|
||||
#include <sigc++/bind.h>
|
||||
#include <sigc++/signal.h>
|
||||
|
|
@ -94,6 +96,23 @@ class LIBPBD_API SystemExec
|
|||
*
|
||||
*/
|
||||
SystemExec (std::string c, char ** a);
|
||||
|
||||
/** similar to \ref SystemExec but expects a whole command line, and
|
||||
* handles some simple escape sequences.
|
||||
*
|
||||
* @param command complete command-line to be executed
|
||||
* @param subs a map of <char, std::string> listing the % substitutions to
|
||||
* be made.
|
||||
*
|
||||
* creates an argv array from the given command string, splitting into
|
||||
* parameters at spaces.
|
||||
* "\ " is non-splitting space, "\\" (and "\" at end of command) as "\",
|
||||
* for "%<char>", <char> is looked up in subs and the corresponding string
|
||||
* substituted. "%%" (and "%" at end of command)
|
||||
* returns an argv array suitable for creating a new SystemExec with
|
||||
*/
|
||||
SystemExec (std::string command, const std::map<char, std::string> subs);
|
||||
|
||||
virtual ~SystemExec ();
|
||||
|
||||
/** fork and execute the given program
|
||||
|
|
@ -182,6 +201,7 @@ class LIBPBD_API SystemExec
|
|||
int nicelevel; ///< process nice level - defaults to 0
|
||||
|
||||
void make_argp(std::string);
|
||||
void make_argp_escaped(std::string command, const std::map<char, std::string> subs);
|
||||
void make_envp();
|
||||
|
||||
char **argp;
|
||||
|
|
@ -198,6 +218,7 @@ class LIBPBD_API SystemExec
|
|||
#else
|
||||
pid_t pid;
|
||||
#endif
|
||||
void init ();
|
||||
pthread_mutex_t write_lock;
|
||||
|
||||
int fdin; ///< file-descriptor for writing to child's STDIN. This variable is identical to pin[1] but also used as status check if the stdin pipe is open: <0 means closed.
|
||||
|
|
|
|||
|
|
@ -151,9 +151,8 @@ static int close_allv(const int except_fds[]) {
|
|||
}
|
||||
#endif /* not on windows, nor vfork */
|
||||
|
||||
|
||||
SystemExec::SystemExec (std::string c, std::string a)
|
||||
: cmd(c)
|
||||
void
|
||||
SystemExec::init ()
|
||||
{
|
||||
pthread_mutex_init(&write_lock, NULL);
|
||||
thread_active=false;
|
||||
|
|
@ -161,12 +160,19 @@ SystemExec::SystemExec (std::string c, std::string a)
|
|||
pin[1] = -1;
|
||||
nicelevel = 0;
|
||||
envp = NULL;
|
||||
argp = NULL;
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
|
||||
stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
|
||||
stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
SystemExec::SystemExec (std::string c, std::string a)
|
||||
: cmd(c)
|
||||
{
|
||||
init ();
|
||||
|
||||
argp = NULL;
|
||||
make_envp();
|
||||
make_argp(a);
|
||||
}
|
||||
|
|
@ -174,21 +180,101 @@ SystemExec::SystemExec (std::string c, std::string a)
|
|||
SystemExec::SystemExec (std::string c, char **a)
|
||||
: cmd(c) , argp(a)
|
||||
{
|
||||
pthread_mutex_init(&write_lock, NULL);
|
||||
thread_active=false;
|
||||
pid = 0;
|
||||
pin[1] = -1;
|
||||
nicelevel = 0;
|
||||
envp = NULL;
|
||||
init ();
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
|
||||
stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
|
||||
stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
|
||||
make_wargs(a);
|
||||
#endif
|
||||
make_envp();
|
||||
}
|
||||
|
||||
SystemExec::SystemExec (std::string command, const std::map<char, std::string> subs)
|
||||
{
|
||||
init ();
|
||||
make_argp_escaped(command, subs);
|
||||
cmd = argp[0];
|
||||
// cmd = strdup(argp[0]);
|
||||
make_envp();
|
||||
}
|
||||
|
||||
void
|
||||
SystemExec::make_argp_escaped(std::string command, const std::map<char, std::string> subs)
|
||||
{
|
||||
|
||||
int inquotes = 0;
|
||||
int n = 0;
|
||||
size_t i = 0;
|
||||
std::string arg = "";
|
||||
|
||||
argp = (char **) malloc(sizeof(char *));
|
||||
|
||||
for (i = 0; i <= command.length(); i++) { // include terminating '\0'
|
||||
char c = command.c_str()[i];
|
||||
if (inquotes) {
|
||||
if (c == '"') {
|
||||
inquotes = 0;
|
||||
} else {
|
||||
// still in quotes - just copy
|
||||
arg += c;
|
||||
}
|
||||
} else switch (c) {
|
||||
case '%' :
|
||||
c = command.c_str()[++i];
|
||||
if (c == '%' || c == '\0') {
|
||||
// "%%", "%" at end-of-string => "%"
|
||||
arg += '%';
|
||||
} else {
|
||||
// search subs for string to substitute for char
|
||||
std::map<char, std::string>::const_iterator s = subs.find(c);
|
||||
if (s != subs.end()) {
|
||||
// found substitution
|
||||
arg += s->second;
|
||||
} else {
|
||||
// not a valid substitution, just copy
|
||||
arg += '%';
|
||||
arg += c;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
c = command.c_str()[++i];
|
||||
switch (c) {
|
||||
case ' ' :
|
||||
case '"' : arg += c; break; // "\\", "\" at end-of-string => "\"
|
||||
case '\0':
|
||||
case '\\': arg += '\\'; break;
|
||||
default : arg += '\\'; arg += c; break;
|
||||
}
|
||||
break;
|
||||
case '"' :
|
||||
inquotes = 1;
|
||||
break;
|
||||
case ' ' :
|
||||
case '\t':
|
||||
case '\0':
|
||||
if (arg.length() > 0) {
|
||||
// if there wasn't already a space or tab, start a new parameter
|
||||
argp = (char **) realloc(argp, (n + 2) * sizeof(char *));
|
||||
argp[n++] = strdup (arg.c_str());
|
||||
arg = "";
|
||||
}
|
||||
break;
|
||||
default :
|
||||
arg += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
argp[n] = NULL;
|
||||
|
||||
char *p = argp[0];
|
||||
n = 0;
|
||||
do {
|
||||
std::cerr << "argv[" << n << "] == \"" << p << "\"" << std::endl;
|
||||
p = argp[n++];
|
||||
} while (p);
|
||||
|
||||
}
|
||||
|
||||
SystemExec::~SystemExec ()
|
||||
{
|
||||
terminate ();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue