get Session::save_as() working much more correctly, and cleaner

This commit is contained in:
Paul Davis 2015-01-14 17:53:23 -05:00
parent 08d56360d6
commit 140778641c
4 changed files with 173 additions and 133 deletions

View File

@ -393,17 +393,29 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
framecnt_t worst_track_latency () const { return _worst_track_latency; }
framecnt_t worst_playback_latency () const { return _worst_output_latency + _worst_track_latency; }
int consolidate_all_media ();
struct SaveAs {
std::string new_parent_folder;
std::string new_name;
bool switch_to;
bool copy_media;
bool copy_external;
std::string new_parent_folder; /* parent folder where new session folder will be created */
std::string new_name; /* name of newly saved session */
bool switch_to; /* true if we should be working on newly saved session after save-as; false otherwise */
bool copy_media; /* true if media files (audio, media, etc) should be copied into newly saved session; false otherwise */
bool copy_external; /* true if external media should be consolidated into the newly saved session; false otherwise */
/* emitted as we make progress */
/* emitted as we make progress. 3 arguments passed to signal
* handler:
*
* 1: percentage complete measured as a fraction (0-1.0) of
* total data copying done.
* 2: number of files copied so far
* 3: total number of files to copy
*
* Handler should return true for save-as to continue, or false
* to stop (and remove all evidence of partial save-as).
*/
PBD::Signal3<bool,float,int64_t,int64_t> Progress;
/* if save_as() returns non-zero, this string will indicate the reason why.
*/
std::string failure_message;
};
int save_as (SaveAs&);
@ -1693,6 +1705,8 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop
void setup_click_state (const XMLNode*);
void setup_bundles ();
void save_as_bring_callback (uint32_t, uint32_t, std::string);
static int get_session_info_from_path (XMLTree& state_tree, const std::string& xmlpath);
};

View File

@ -337,7 +337,6 @@ FileSource::find (Session& s, DataType type, const string& path, bool must_exist
}
found_path = keeppath;
ret = true;
out:

View File

@ -176,6 +176,7 @@ SessionDirectory::sub_directories () const
tmp_paths.push_back (sound_path ());
tmp_paths.push_back (midi_path ());
tmp_paths.push_back (video_path ());
tmp_paths.push_back (peak_path ());
tmp_paths.push_back (dead_path ());
tmp_paths.push_back (export_path ());

View File

@ -3935,6 +3935,13 @@ bool accept_all_files (string const &, void *)
return true;
}
void
Session::save_as_bring_callback (uint32_t,uint32_t,string)
{
/* It would be good if this did something useful vis-a-vis save-as, but the arguments doesn't provide the correct information right now to do this.
*/
}
int
Session::save_as (SaveAs& saveas)
{
@ -3946,6 +3953,14 @@ Session::save_as (SaveAs& saveas)
int64_t copied = 0;
int64_t cnt = 0;
int64_t all = 0;
int32_t internal_file_cnt = 0;
vector<string> do_not_copy_extensions;
do_not_copy_extensions.push_back (statefile_suffix);
do_not_copy_extensions.push_back (pending_suffix);
do_not_copy_extensions.push_back (backup_suffix);
do_not_copy_extensions.push_back (temp_suffix);
do_not_copy_extensions.push_back (history_suffix);
/* get total size */
@ -3961,99 +3976,141 @@ Session::save_as (SaveAs& saveas)
all += files.size();
cerr << (*sd).path << " Contained " << files.size() << " total now " << all << endl;
for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
GStatBuf gsb;
if ((*i).find (X_("interchange")) == string::npos || saveas.copy_media) {
g_stat ((*i).c_str(), &gsb);
total_bytes += gsb.st_size;
}
g_stat ((*i).c_str(), &gsb);
total_bytes += gsb.st_size;
}
cerr << "\ttotal size now " << total_bytes << endl;
}
/* Create the new session directory */
/* save old values so we can switch back if we are not switching to the new session */
string old_path = _path;
string old_name = _name;
string old_snapshot = _current_snapshot_name;
string old_sd = _session_dir->root_path();
vector<string> old_search_path[DataType::num_types];
string old_config_search_path[DataType::num_types];
old_search_path[DataType::AUDIO] = source_search_path (DataType::AUDIO);
old_search_path[DataType::MIDI] = source_search_path (DataType::MIDI);
old_config_search_path[DataType::AUDIO] = config.get_audio_search_path ();
old_config_search_path[DataType::MIDI] = config.get_midi_search_path ();
/* switch session directory */
(*_session_dir) = to_dir;
/* create new tree */
if (!_session_dir->create()) {
return -1;
}
cerr << "Created new session dir " << _session_dir->root_path() << endl;
try {
if (saveas.copy_media) {
/* copy all media files. Find each location in
* session_dirs, and copy files from there to
* target.
/* copy all media files. Find each location in
* session_dirs, and copy files from there to
* target.
*/
for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
/* need to clear this because
* find_files_matching_filter() is cumulative
*/
for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
/* need to clear this because
* find_files_matching_filter() is cumulative
*/
files.clear ();
find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
const size_t prefix_len = (*sd).path.size();
files.clear ();
const size_t prefix_len = (*sd).path.size();
/* Work just on the files within this session dir */
find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
/* copy all the files. Handling is different for media files
than others because of the *silly* subtree we have below the interchange
folder. That really was a bad idea, but I'm not fixing it as part of
implementing ::save_as().
*/
for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
/* copy all media files (everything below
* interchange/)
*/
std::string from = *i;
for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
std::string from = *i;
if ((*i).find (interchange_dir_name) != string::npos) {
/* media file */
if ((*i).find (X_("interchange")) != string::npos) {
/* media file */
if (saveas.copy_media) {
GStatBuf gsb;
g_stat ((*i).c_str(), &gsb);
/* strip the session dir prefix from
* each full path, then prepend new
* to_dir to give complete path.
/* typedir is the "midifiles" or "audiofiles" etc. part of the path.
*/
string typedir = Glib::path_get_basename (Glib::path_get_dirname (*i));
vector<string> v;
v.push_back (to_dir);
v.push_back (interchange_dir_name);
v.push_back (new_folder);
v.push_back (typedir);
v.push_back (Glib::path_get_basename (*i));
std::string to = Glib::build_filename (to_dir, (*i).substr (prefix_len));
cerr << "Copy " << from << " to " << to << endl;
std::string to = Glib::build_filename (v);
if (!copy_file (from, to)) {
throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
}
copied += gsb.st_size;
double fraction = (double) copied / total_bytes;
/* tell someone "X percent, file M of
* N"; M is one-based
*/
cnt++;
cerr << "PROGRESS " << fraction << "%, " << cnt << " of " << all << endl;
#if 0
if (!saveas.Progress (fraction, cnt, all)) {
throw Glib::FileError (Glib::FileError::FAILED, "copy cancelled");
}
#endif
}
/* we found media files inside the session folder */
internal_file_cnt++;
} else {
/* normal non-media file. Don't copy state, history, etc.
*/
bool do_copy = true;
for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
if (((*i).length() > (*v).length()) && ((*i).find (*v) == (*i).length() - (*v).length())) {
/* end of filename matches extension, do not copy file */
do_copy = false;
break;
}
}
if (do_copy) {
string to = Glib::build_filename (to_dir, (*i).substr (prefix_len));
if (!copy_file (from, to)) {
throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
}
}
}
/* measure file size even if we're not going to copy so that our Progress
signals are correct, since we included these do-not-copy files
in the computation of the total size and file count.
*/
GStatBuf gsb;
g_stat ((*i).c_str(), &gsb);
copied += gsb.st_size;
cnt++;
double fraction = (double) copied / total_bytes;
/* tell someone "X percent, file M of N"; M is one-based */
boost::optional<bool> res = saveas.Progress (fraction, cnt, all);
bool keep_going = true;
if (res) {
keep_going = *res;
}
if (!keep_going) {
throw Glib::FileError (Glib::FileError::FAILED, "copy cancelled");
}
}
}
@ -4061,65 +4118,31 @@ Session::save_as (SaveAs& saveas)
_path = to_dir;
_current_snapshot_name = saveas.new_name;
_name = saveas.new_name;
cerr << "New path = " << _path << endl;
if (!saveas.copy_media && saveas.switch_to) {
/* need to make all internal file sources point to old session */
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
if (!saveas.copy_media) {
if (fs && fs->within_session()) {
/* reset search paths of the new session (which we're pretending to be right now) to
include the original session search path, so we can still find all audio.
*/
cerr << "fs " << fs->name() << " is inside session " << fs->path() << endl;
/* give it an absolute path referencing
* the original session. Should be
* easy, but in general, we don't
* actually know where it lives - it
* could be in any session dir. So we
* have to look for it.
*
* Note that the session_dirs list
*/
if (internal_file_cnt) {
for (vector<string>::iterator s = old_search_path[DataType::AUDIO].begin(); s != old_search_path[DataType::AUDIO].end(); ++s) {
ensure_search_path_includes (*s, DataType::AUDIO);
}
for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
SessionDirectory sdir ((*sd).path);
string file_dir;
switch (fs->type()) {
case DataType::AUDIO:
file_dir = sdir.sound_path();
break;
case DataType::MIDI:
file_dir = sdir.midi_path();
break;
default:
continue;
}
string possible_path = Glib::build_filename (file_dir, fs->path());
if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) {
/* Found it */
cerr << "Reset path for " << fs->name() << " @ " << fs->path() << " to " << possible_path << endl;
fs->set_path (possible_path);
break;
}
}
for (vector<string>::iterator s = old_search_path[DataType::MIDI].begin(); s != old_search_path[DataType::MIDI].end(); ++s) {
ensure_search_path_includes (*s, DataType::MIDI);
}
}
}
bool was_dirty = dirty ();
cerr << "Saving state\n";
save_state ("", false, false);
save_default_options ();
if (saveas.copy_media && saveas.copy_external) {
if (consolidate_all_media()) {
if (bring_all_sources_into_session (boost::bind (&Session::save_as_bring_callback, this, _1, _2, _3))) {
throw Glib::FileError (Glib::FileError::NO_SPACE_LEFT, "consolidate failed");
}
}
@ -4138,9 +4161,15 @@ Session::save_as (SaveAs& saveas)
set_dirty ();
}
if (internal_file_cnt) {
/* reset these to their original values */
config.set_audio_search_path (old_config_search_path[DataType::AUDIO]);
config.set_midi_search_path (old_config_search_path[DataType::MIDI]);
}
} else {
/* prune session dirs
/* prune session dirs, and update disk space statistics
*/
space_and_path sp;
@ -4148,14 +4177,23 @@ Session::save_as (SaveAs& saveas)
session_dirs.clear ();
session_dirs.push_back (sp);
refresh_disk_space ();
cerr << "pruned session dirs, sd = " << _session_dir->root_path()
<< " path = " << _path << endl;
}
} catch (Glib::FileError& e) {
saveas.failure_message = e.what();
/* recursively remove all the directories */
remove_directory (to_dir);
/* return error */
return -1;
} catch (...) {
cerr << "copying/saveas failed\n";
saveas.failure_message = _("unknown reason");
/* recursively remove all the directories */
@ -4165,18 +4203,6 @@ Session::save_as (SaveAs& saveas)
return -1;
}
cerr << "saveas completed successfully\n";
return 0;
}
/** Check all sources used by the current snapshot
* and make a copy of any external media within
* the session.
*/
int
Session::consolidate_all_media ()
{
return 0;
}