get Session::save_as() working much more correctly, and cleaner
authorPaul Davis <paul@linuxaudiosystems.com>
Wed, 14 Jan 2015 22:53:23 +0000 (17:53 -0500)
committerPaul Davis <paul@linuxaudiosystems.com>
Wed, 14 Jan 2015 22:53:23 +0000 (17:53 -0500)
libs/ardour/ardour/session.h
libs/ardour/file_source.cc
libs/ardour/session_directory.cc
libs/ardour/session_state.cc

index 83af5c2da6bf26d28d280be97546af68b17b71aa..d769db25e696697b07198aa45e93108e5c963948 100644 (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);
 };
 
index 0aec49e6bdd74899dbfafa28d903d0aa6047fc75..b4ecb157c5038e916a22e0a2391b4fa5559c15a1 100644 (file)
@@ -337,7 +337,6 @@ FileSource::find (Session& s, DataType type, const string& path, bool must_exist
         }
 
         found_path = keeppath;
-
         ret = true;
 
   out:
index 9d0be414da7edee302ed307da83a435cf849713e..b6536ebec968bd55379c675ea7c75270cfeb56f8 100644 (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 ());
index be4a7cb1cf1285d1431c758d38d20e724a905ba2..45693960c22436a14f0d7b188754eb22f260eb45 100644 (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,165 +3976,173 @@ 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 (X_("interchange")) != string::npos) {
+                               if ((*i).find (interchange_dir_name) != string::npos) {
+                                       
+                                       /* media file */
 
-                                               /* media file */
-                                               
-                                               GStatBuf gsb;
-                                               g_stat ((*i).c_str(), &gsb);
+                                       if (saveas.copy_media) {
                                                
-                                               /* 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");
                                                }
+                                       }
+                                       
+                                       /* 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));
                                                
-                                               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");
+                                               if (!copy_file (from, to)) {
+                                                       throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
                                                }
-#endif
                                        }
                                }
+                               
+                               /* 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");
+                               }
                        }
                }
 
                _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;
-}