get Session::save_as() working much more correctly, and cleaner
This commit is contained in:
parent
08d56360d6
commit
140778641c
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -337,7 +337,6 @@ FileSource::find (Session& s, DataType type, const string& path, bool must_exist
|
|||
}
|
||||
|
||||
found_path = keeppath;
|
||||
|
||||
ret = true;
|
||||
|
||||
out:
|
||||
|
|
|
@ -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 ());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue