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:
Colin Fletcher 2014-05-19 20:54:36 +01:00
commit 5399425f53
29 changed files with 992 additions and 367 deletions

View 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>

View 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>

View file

@ -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.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)); 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.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)); 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.set_label (string_compose (_("Track output (channels: %1)"), track_chans));
processed_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection)); 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(); sync_with_manager();
vbox.show_all_children (); vbox.show_all_children ();
@ -541,7 +541,7 @@ TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * sessio
// Options // Options
options_box.pack_start(region_contents_button); options_box.pack_start(region_contents_button);
options_box.pack_start(track_output_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
track_scroller.add (track_view); track_scroller.add (track_view);

View file

@ -126,7 +126,7 @@ class PortExportChannelSelector : public ExportChannelSelector
typedef Gtk::TreeModelColumn<Glib::RefPtr<Gtk::ListStore> > ComboCol; typedef Gtk::TreeModelColumn<Glib::RefPtr<Gtk::ListStore> > ComboCol;
ComboCol port_list_col; 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 { struct Channel {
public: public:

View file

@ -140,69 +140,27 @@ ExportDialog::init ()
progress_widget.hide_all(); progress_widget.hide_all();
} }
void
ExportDialog::expanded_changed ()
{
set_resizable(advanced->get_expanded());
}
void void
ExportDialog::init_gui () ExportDialog::init_gui ()
{ {
Gtk::Alignment * preset_align = Gtk::manage (new Gtk::Alignment()); Gtk::Alignment * preset_align = Gtk::manage (new Gtk::Alignment());
preset_align->add (*preset_selector); preset_align->add (*preset_selector);
preset_align->set_padding (0, 12, 0, 0); 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()); export_notebook.append_page (*file_format_selector, _("File format"));
timespan_vbox->set_spacing (12); export_notebook.append_page (*timespan_selector, _("Time Span"));
timespan_vbox->set_border_width (12); export_notebook.append_page (*channel_selector, _("Channels"));
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::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);
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 void
@ -211,6 +169,7 @@ ExportDialog::init_components ()
preset_selector.reset (new ExportPresetSelector ()); preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager)); timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager)); channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ()); file_notebook.reset (new ExportFileNotebook ());
} }
@ -300,11 +259,34 @@ ExportDialog::show_conflicting_files ()
dialog.run(); dialog.run();
} }
void
ExportDialog::soundcloud_upload_progress(double total, double now, std::string title)
{
soundcloud_selector->do_progress_callback(total, now, title);
}
void void
ExportDialog::do_export () ExportDialog::do_export ()
{ {
try { try {
profile_manager->prepare_for_export (); 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 (); handler->do_export ();
show_progress (); show_progress ();
} catch(std::exception & e) { } catch(std::exception & e) {
@ -418,6 +400,7 @@ ExportRangeDialog::init_components ()
preset_selector.reset (new ExportPresetSelector ()); preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, range_id)); timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, range_id));
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager)); channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ()); file_notebook.reset (new ExportFileNotebook ());
} }
@ -431,6 +414,7 @@ ExportSelectionDialog::init_components ()
preset_selector.reset (new ExportPresetSelector ()); preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, X_("selection"))); timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, X_("selection")));
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager)); channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ()); file_notebook.reset (new ExportFileNotebook ());
} }
@ -444,8 +428,7 @@ void
ExportRegionDialog::init_gui () ExportRegionDialog::init_gui ()
{ {
ExportDialog::init_gui (); ExportDialog::init_gui ();
export_notebook.set_tab_label_text(*export_notebook.get_nth_page(2), _("Source"));
channels_label->set_text (_("Source"));
} }
void void
@ -456,6 +439,7 @@ ExportRegionDialog::init_components ()
preset_selector.reset (new ExportPresetSelector ()); preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, loc_id)); timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, loc_id));
channel_selector.reset (new RegionExportChannelSelector (_session, profile_manager, region, track)); channel_selector.reset (new RegionExportChannelSelector (_session, profile_manager, region, track));
soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ()); file_notebook.reset (new ExportFileNotebook ());
} }
@ -471,5 +455,6 @@ StemExportDialog::init_components ()
preset_selector.reset (new ExportPresetSelector ()); preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager)); timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
channel_selector.reset (new TrackExportChannelSelector (_session, profile_manager)); channel_selector.reset (new TrackExportChannelSelector (_session, profile_manager));
soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ()); file_notebook.reset (new ExportFileNotebook ());
} }

View file

@ -32,6 +32,7 @@
#include "export_file_notebook.h" #include "export_file_notebook.h"
#include "export_preset_selector.h" #include "export_preset_selector.h"
#include "ardour_dialog.h" #include "ardour_dialog.h"
#include "soundcloud_export_selector.h"
#include <gtkmm.h> #include <gtkmm.h>
@ -43,7 +44,8 @@ namespace ARDOUR {
class ExportTimespanSelector; class ExportTimespanSelector;
class ExportChannelSelector; class ExportChannelSelector;
class ExportDialog : public ArdourDialog { class ExportDialog : public ArdourDialog, public PBD::ScopedConnectionList
{
public: public:
@ -75,26 +77,22 @@ class ExportDialog : public ArdourDialog {
// Must initialize all the shared_ptrs below // Must initialize all the shared_ptrs below
virtual void init_components (); 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<ExportPresetSelector> preset_selector;
boost::scoped_ptr<ExportTimespanSelector> timespan_selector; boost::scoped_ptr<ExportTimespanSelector> timespan_selector;
boost::scoped_ptr<ExportChannelSelector> channel_selector; boost::scoped_ptr<ExportChannelSelector> channel_selector;
boost::scoped_ptr<ExportFileNotebook> file_notebook; boost::scoped_ptr<ExportFileNotebook> file_notebook;
boost::scoped_ptr<SoundcloudExportSelector> soundcloud_selector;
Gtk::VBox warning_widget; Gtk::VBox warning_widget;
Gtk::VBox progress_widget; Gtk::VBox progress_widget;
Gtk::Label * timespan_label; /*** GUI components ***/
Gtk::Label * channels_label; Gtk::Notebook export_notebook;
private: private:
void init (); void init ();
void expanded_changed();
void notify_errors (bool force = false); void notify_errors (bool force = false);
void close_dialog (); void close_dialog ();
@ -112,10 +110,7 @@ class ExportDialog : public ArdourDialog {
PublicEditor & editor; PublicEditor & editor;
StatusPtr status; StatusPtr status;
/*** GUI components ***/
Glib::RefPtr<Gtk::SizeGroup> advanced_sizegroup;
Gtk::Expander * advanced;
/* Warning area */ /* Warning area */
@ -138,6 +133,8 @@ class ExportDialog : public ArdourDialog {
float previous_progress; // Needed for gtk bug workaround float previous_progress; // Needed for gtk bug workaround
void soundcloud_upload_progress(double total, double now, std::string title);
/* Buttons */ /* Buttons */
Gtk::Button * cancel_button; Gtk::Button * cancel_button;
@ -170,9 +167,6 @@ class ExportRegionDialog : public ExportDialog
public: public:
ExportRegionDialog (PublicEditor & editor, ARDOUR::AudioRegion const & region, ARDOUR::AudioTrack & track); ExportRegionDialog (PublicEditor & editor, ARDOUR::AudioRegion const & region, ARDOUR::AudioTrack & track);
protected:
virtual bool channel_selector_is_expandable() { return false; }
private: private:
void init_gui (); void init_gui ();
void init_components (); void init_components ();

View file

@ -51,6 +51,9 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
silence_end_checkbox (_("Add silence at end:")), silence_end_checkbox (_("Add silence at end:")),
silence_end_clock ("silence_end", true, "", true, false, true), 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), format_table (3, 4),
compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT), compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT),
quality_label (_("Quality"), 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_checkbox, 1, 2, 2, 3);
silence_table.attach (silence_end_clock, 2, 3, 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 */ /* Format table */
init_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_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)); 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_cue, false, false);
cue_toc_vbox.pack_start (with_toc, 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()); tag_checkbox.set_active (spec->tag());
upload_checkbox.set_active (spec->upload());
command_entry.set_text (spec->command());
} }
void void
@ -717,6 +728,18 @@ ExportFormatDialog::update_with_toc ()
manager.select_with_toc (with_toc.get_active()); 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 void
ExportFormatDialog::update_description() ExportFormatDialog::update_description()
{ {

View file

@ -179,6 +179,12 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
Gtk::CheckButton silence_end_checkbox; Gtk::CheckButton silence_end_checkbox;
AudioClock silence_end_clock; AudioClock silence_end_clock;
/* Upload */
Gtk::CheckButton upload_checkbox;
Gtk::Label command_label;
Gtk::Entry command_entry;
/* Format table */ /* Format table */
struct CompatibilityCols : public Gtk::TreeModelColumnRecord struct CompatibilityCols : public Gtk::TreeModelColumnRecord
@ -311,6 +317,8 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
void update_with_toc (); void update_with_toc ();
void update_with_cue (); void update_with_cue ();
void update_upload ();
void update_command ();
Gtk::TreeView sample_format_view; Gtk::TreeView sample_format_view;
Gtk::TreeView dither_type_view; Gtk::TreeView dither_type_view;

View file

@ -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;
}

View file

@ -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__

View file

@ -105,6 +105,9 @@ ExportTimespanSelector::ExportTimespanSelector (ARDOUR::Session * session, Profi
/* Range view */ /* Range view */
range_list = Gtk::ListStore::create (range_cols); 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_model (range_list);
range_view.set_headers_visible (true); 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 void
ExportTimespanSelector::add_range_to_selection (ARDOUR::Location const * loc) ExportTimespanSelector::add_range_to_selection (ARDOUR::Location const * loc)
{ {

View file

@ -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 update_range_name (std::string const & path, std::string const & new_text);
void set_selection_state_of_all_timespans (bool); void set_selection_state_of_all_timespans (bool);
int location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b);
/*** GUI components ***/ /*** GUI components ***/
@ -132,7 +133,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
Gtk::ScrolledWindow range_scroller; Gtk::ScrolledWindow range_scroller;
}; };
/// Allows seleting multiple timespans /// Allows selecting multiple timespans
class ExportTimespanSelectorMultiple : public ExportTimespanSelector class ExportTimespanSelectorMultiple : public ExportTimespanSelector
{ {
public: public:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View 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;
}

View 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;
};

View file

@ -204,6 +204,7 @@ gtk2_ardour_sources = [
'session_option_editor.cc', 'session_option_editor.cc',
'sfdb_ui.cc', 'sfdb_ui.cc',
'shuttle_control.cc', 'shuttle_control.cc',
'soundcloud_export_selector.cc',
'splash.cc', 'splash.cc',
'speaker_dialog.cc', 'speaker_dialog.cc',
'startup.cc', 'startup.cc',

View file

@ -100,6 +100,8 @@ class LIBARDOUR_API ExportFormatManager : public PBD::ScopedConnectionList
void select_with_cue (bool); void select_with_cue (bool);
void select_with_toc (bool); void select_with_toc (bool);
void select_upload (bool);
void set_command (std::string);
void select_src_quality (ExportFormatBase::SRCQuality value); void select_src_quality (ExportFormatBase::SRCQuality value);
void select_trim_beginning (bool value); void select_trim_beginning (bool value);
void select_silence_beginning (AnyTime const & time); void select_silence_beginning (AnyTime const & time);

View file

@ -96,6 +96,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
void set_tag (bool tag_it) { _tag = tag_it; } void set_tag (bool tag_it) { _tag = tag_it; }
void set_with_cue (bool yn) { _with_cue = yn; } void set_with_cue (bool yn) { _with_cue = yn; }
void set_with_toc (bool yn) { _with_toc = 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_beginning (AnyTime const & value) { _silence_beginning = value; }
void set_silence_end (AnyTime const & value) { _silence_end = 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; } float normalize_target () const { return _normalize_target; }
bool with_toc() const { return _with_toc; } bool with_toc() const { return _with_toc; }
bool with_cue() const { return _with_cue; } 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; } bool tag () const { return _tag && supports_tagging; }
@ -174,6 +178,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
float _normalize_target; float _normalize_target;
bool _with_toc; bool _with_toc;
bool _with_cue; bool _with_cue;
bool _upload;
std::string _command;
/* serialization helpers */ /* serialization helpers */

View file

@ -31,6 +31,7 @@
#include "ardour/session.h" #include "ardour/session.h"
#include "ardour/libardour_visibility.h" #include "ardour/libardour_visibility.h"
#include "ardour/types.h" #include "ardour/types.h"
#include "pbd/signals.h"
namespace AudioGrapher { namespace AudioGrapher {
class BroadcastInfo; class BroadcastInfo;
@ -68,7 +69,7 @@ class LIBARDOUR_API ExportElementFactory
Session & session; Session & session;
}; };
class LIBARDOUR_API ExportHandler : public ExportElementFactory class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::trackable
{ {
public: public:
struct FileSpec { struct FileSpec {
@ -95,6 +96,8 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory
friend boost::shared_ptr<ExportHandler> Session::get_export_handler(); friend boost::shared_ptr<ExportHandler> Session::get_export_handler();
ExportHandler (Session & session); ExportHandler (Session & session);
void command_output(std::string output, size_t size);
public: public:
~ExportHandler (); ~ExportHandler ();
@ -105,6 +108,17 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory
std::string get_cd_marker_filename(std::string filename, CDMarkerFormat format); 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: private:
void handle_duplicate_format_extensions(); void handle_duplicate_format_extensions();

View 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__ */

View file

@ -32,6 +32,7 @@ class LIBARDOUR_API SystemExec
public: public:
SystemExec (std::string c, std::string a = ""); SystemExec (std::string c, std::string a = "");
SystemExec (std::string c, char ** a); SystemExec (std::string c, char ** a);
SystemExec (std::string c, const std::map<char, std::string> subs);
~SystemExec (); ~SystemExec ();
int start (int stderr_mode = 1) { int start (int stderr_mode = 1) {

View file

@ -293,6 +293,20 @@ ExportFormatManager::select_with_toc (bool value)
check_for_description_change (); 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 void
ExportFormatManager::select_trim_beginning (bool value) ExportFormatManager::select_trim_beginning (bool value)
{ {

View file

@ -170,6 +170,8 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s)
, _normalize_target (1.0) , _normalize_target (1.0)
, _with_toc (false) , _with_toc (false)
, _with_cue (false) , _with_cue (false)
, _upload (false)
, _command ("")
{ {
format_ids.insert (F_None); format_ids.insert (F_None);
endiannesses.insert (E_FileDefault); endiannesses.insert (E_FileDefault);
@ -244,6 +246,8 @@ ExportFormatSpecification::get_state ()
root->add_property ("id", _id.to_s()); root->add_property ("id", _id.to_s());
root->add_property ("with-cue", _with_cue ? "true" : "false"); root->add_property ("with-cue", _with_cue ? "true" : "false");
root->add_property ("with-toc", _with_toc ? "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 = root->add_child ("Encoding");
node->add_property ("id", enum_2_string (format_id())); node->add_property ("id", enum_2_string (format_id()));
@ -321,6 +325,18 @@ ExportFormatSpecification::set_state (const XMLNode & root)
_with_toc = false; _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 */ /* Encoding and SRC */
if ((child = root.child ("Encoding"))) { if ((child = root.child ("Encoding"))) {
@ -590,6 +606,14 @@ ExportFormatSpecification::description (bool include_name)
components.push_back ("CUE"); components.push_back ("CUE");
} }
if (_upload) {
components.push_back ("Upload");
}
if (!_command.empty()) {
components.push_back ("+");
}
string desc; string desc;
if (include_name) { if (include_name) {
desc = _name + ": "; desc = _name + ": ";

View file

@ -33,6 +33,10 @@
#include "ardour/export_status.h" #include "ardour/export_status.h"
#include "ardour/export_format_specification.h" #include "ardour/export_format_specification.h"
#include "ardour/export_filename.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 "ardour/session_metadata.h"
#include "i18n.h" #include "i18n.h"
@ -277,6 +281,13 @@ ExportHandler::process_normalize ()
return 0; return 0;
} }
void
ExportHandler::command_output(std::string output, size_t size)
{
std::cerr << "command: " << size << ", " << output << std::endl;
info << output << endmsg;
}
void void
ExportHandler::finish_timespan () ExportHandler::finish_timespan ()
{ {
@ -297,13 +308,77 @@ ExportHandler::finish_timespan ()
AudiofileTagger::tag_file(filename, *SessionMetadata::Metadata()); 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()); config_map.erase (config_map.begin());
} }
start_timespan (); start_timespan ();
} }
/*** CD Marker sutff ***/ /*** CD Marker stuff ***/
struct LocationSortByStart { struct LocationSortByStart {
bool operator() (Location *a, Location *b) { bool operator() (Location *a, Location *b) {

View 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);
}

View file

@ -65,4 +65,14 @@ SystemExec::SystemExec (std::string c, std::string a)
#endif #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() { } SystemExec::~SystemExec() { }

View file

@ -194,6 +194,7 @@ libardour_sources = [
'sndfile_helpers.cc', 'sndfile_helpers.cc',
'sndfileimportable.cc', 'sndfileimportable.cc',
'sndfilesource.cc', 'sndfilesource.cc',
'soundcloud_upload.cc',
'source.cc', 'source.cc',
'source_factory.cc', 'source_factory.cc',
'speakers.cc', 'speakers.cc',

View file

@ -42,6 +42,8 @@
#include <string> #include <string>
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
#include <map>
#ifdef NOPBD /* unit-test outside ardour */ #ifdef NOPBD /* unit-test outside ardour */
#include <sigc++/bind.h> #include <sigc++/bind.h>
#include <sigc++/signal.h> #include <sigc++/signal.h>
@ -94,6 +96,23 @@ class LIBPBD_API SystemExec
* *
*/ */
SystemExec (std::string c, char ** a); 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 (); virtual ~SystemExec ();
/** fork and execute the given program /** fork and execute the given program
@ -182,6 +201,7 @@ class LIBPBD_API SystemExec
int nicelevel; ///< process nice level - defaults to 0 int nicelevel; ///< process nice level - defaults to 0
void make_argp(std::string); void make_argp(std::string);
void make_argp_escaped(std::string command, const std::map<char, std::string> subs);
void make_envp(); void make_envp();
char **argp; char **argp;
@ -198,6 +218,7 @@ class LIBPBD_API SystemExec
#else #else
pid_t pid; pid_t pid;
#endif #endif
void init ();
pthread_mutex_t write_lock; 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. 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.

View file

@ -151,9 +151,8 @@ static int close_allv(const int except_fds[]) {
} }
#endif /* not on windows, nor vfork */ #endif /* not on windows, nor vfork */
void
SystemExec::SystemExec (std::string c, std::string a) SystemExec::init ()
: cmd(c)
{ {
pthread_mutex_init(&write_lock, NULL); pthread_mutex_init(&write_lock, NULL);
thread_active=false; thread_active=false;
@ -161,12 +160,19 @@ SystemExec::SystemExec (std::string c, std::string a)
pin[1] = -1; pin[1] = -1;
nicelevel = 0; nicelevel = 0;
envp = NULL; envp = NULL;
argp = NULL;
#ifdef PLATFORM_WINDOWS #ifdef PLATFORM_WINDOWS
stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE; stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE; stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE; stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
#endif #endif
}
SystemExec::SystemExec (std::string c, std::string a)
: cmd(c)
{
init ();
argp = NULL;
make_envp(); make_envp();
make_argp(a); make_argp(a);
} }
@ -174,21 +180,101 @@ SystemExec::SystemExec (std::string c, std::string a)
SystemExec::SystemExec (std::string c, char **a) SystemExec::SystemExec (std::string c, char **a)
: cmd(c) , argp(a) : cmd(c) , argp(a)
{ {
pthread_mutex_init(&write_lock, NULL); init ();
thread_active=false;
pid = 0;
pin[1] = -1;
nicelevel = 0;
envp = NULL;
#ifdef PLATFORM_WINDOWS #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); make_wargs(a);
#endif #endif
make_envp(); 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 () SystemExec::~SystemExec ()
{ {
terminate (); terminate ();
@ -522,7 +608,7 @@ SystemExec::make_argp(std::string args) {
*cp2 = '\0'; *cp2 = '\0';
argp[argn++] = strdup(cp1); argp[argn++] = strdup(cp1);
cp1 = cp2 + 1; cp1 = cp2 + 1;
argp = (char **) realloc(argp, (argn + 1) * sizeof(char *)); argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
} }
} }
if (cp2 != cp1) { if (cp2 != cp1) {