1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 07:40:23 +02:00

Move ProjectFSCK out of DirManager...

... which makes DirManager.cpp not depend on MissingAliasFileDialog.cpp
This commit is contained in:
Paul Licameli 2019-05-14 13:24:14 -04:00
parent e5c7e5a21f
commit 2faa24c96b
13 changed files with 475 additions and 399 deletions

View File

@ -124,7 +124,7 @@ void FindDependencies(AudacityProject *project,
auto aliasBlockFile = static_cast<AliasBlockFile*>( &*f );
const wxFileName &fileName = aliasBlockFile->GetAliasedFileName();
// In DirManager::ProjectFSCK(), if the user has chosen to
// In ProjectFSCK(), if the user has chosen to
// "Replace missing audio with silence", the code there puts in an empty wxFileName.
// Don't count those in dependencies.
if (!fileName.IsOk())

View File

@ -93,12 +93,10 @@
#include "blockfile/ODPCMAliasBlockFile.h"
#include "blockfile/ODDecodeBlockFile.h"
#include "InconsistencyException.h"
#include "MissingAliasFileDialog.h"
#include "Project.h"
#include "Prefs.h"
#include "Sequence.h"
#include "widgets/Warning.h"
#include "widgets/MultiDialog.h"
#include "widgets/ErrorDialog.h"
#include "widgets/ProgressDialog.h"
@ -152,14 +150,14 @@ wxMemorySize GetFreeMemory()
// JKC: Using flag wxDIR_NO_FOLLOW to NOT follow symbolic links.
// Directories and files inside a project should never be symbolic
// links, so if we find one, do not follow it.
static int RecursivelyEnumerate(const FilePath &dirPath,
int DirManager::RecursivelyEnumerate(const FilePath &dirPath,
FilePaths& filePathArray, // output: all files in dirPath tree
wxString dirspec,
wxString filespec,
bool bFiles, bool bDirs,
int progress_count = 0,
int progress_bias = 0,
ProgressDialog* progress = NULL)
int progress_count,
int progress_bias,
ProgressDialog* progress)
{
int count=0;
bool cont;
@ -206,7 +204,7 @@ static int RecursivelyEnumerate(const FilePath &dirPath,
return count;
}
static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
int DirManager::RecursivelyEnumerateWithProgress(const FilePath &dirPath,
FilePaths& filePathArray, // output: all files in dirPath tree
wxString dirspec,
wxString filespec,
@ -228,7 +226,7 @@ static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
return count;
}
static int RecursivelyCountSubdirs( const FilePath &dirPath )
int DirManager::RecursivelyCountSubdirs( const FilePath &dirPath )
{
bool bContinue;
int nCount = 0;
@ -248,9 +246,9 @@ static int RecursivelyCountSubdirs( const FilePath &dirPath )
return nCount;
}
static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
int nDirCount = 0,
ProgressDialog* pProgress = NULL)
int DirManager::RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
int nDirCount,
ProgressDialog* pProgress)
{
bool bContinue;
wxDir dir(dirPath);
@ -293,8 +291,8 @@ static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
return nCount;
}
static void RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
int flags, const wxChar* message = NULL)
void DirManager::RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
int flags, const wxChar* message)
{
bool bFiles= (flags & kCleanFiles) != 0;
bool bDirs = (flags & kCleanDirs) != 0;
@ -1645,370 +1643,6 @@ bool DirManager::EnsureSafeFilename(const wxFileName &fName)
return true;
}
// Check the BlockFiles against the disk state.
// Missing Blockfile data can be regenerated if possible or replaced with silence.
// Orphan blockfiles can be deleted.
// Note that even BlockFiles not referenced by the current savefile (but locked
// by history) will be reflected in the mBlockFileHash, and that's a
// good thing; this is one reason why we use the hash and not the most
// recent savefile.
int DirManager::ProjectFSCK(const bool bForceError, const bool bAutoRecoverMode)
{
// In earlier versions of this method, enumerations of errors were
// all done in sequence, then the user was prompted for each type of error.
// The enumerations are now interleaved with prompting, because, for example,
// user choosing to replace missing aliased block files with silence
// needs to put in SilentBlockFiles and DELETE the corresponding auf files,
// so those would then not be cumulated in missingAUFHash.
// We still do the FindX methods outside the conditionals,
// so the log always shows all found errors.
int action; // choice of action for each type of error
int nResult = 0;
if (bForceError && !bAutoRecoverMode)
{
// TODO: Replace with more user friendly error message?
/* i18n-hint: The audacity project file is XML and has 'tags' in it,
rather like html tags <something>some stuff</something>.
This error message is about the tags that hold the sequence information.
The error message is confusing to users in English, and could just say
"Found problems with <sequence> when checking project file." */
wxString msg = _("Project check read faulty Sequence tags.");
const wxChar *buttons[] =
{_("Close project immediately with no changes"),
_("Continue with repairs noted in log, and check for more errors. This will save the project in its current state, unless you \"Close project immediately\" on further error alerts."),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Problems Reading Sequence Tags"), buttons);
if (action == 0)
nResult = FSCKstatus_CLOSE_REQ;
else
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
}
FilePaths filePathArray; // *all* files in the project directory/subdirectories
auto dirPath = (!projFull.empty() ? projFull : mytemp);
RecursivelyEnumerateWithProgress(
dirPath,
filePathArray, // output: all files in project directory tree
wxEmptyString, // All dirs
wxEmptyString, // All files
true, false,
mBlockFileHash.size(), // rough guess of how many BlockFiles will be found/processed, for progress
_("Inspecting project file data"));
//
// MISSING ALIASED AUDIO FILES
//
MissingAliasFilesDialog::SetShouldShow(false);
BlockHash missingAliasFilesAUFHash; // (.auf) AliasBlockFiles whose aliased files are missing
BlockHash missingAliasFilesPathHash; // full paths of missing aliased files
this->FindMissingAliasFiles(missingAliasFilesAUFHash, missingAliasFilesPathHash);
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAliasFilesAUFHash.empty())
{
// In auto-recover mode, we always create silent blocks, and do not ask user.
// This makes sure the project is complete next time we open it.
if (bAutoRecoverMode)
action = 2;
else
{
wxString msgA =
_("Project check of \"%s\" folder \
\ndetected %lld missing external audio file(s) \
\n('aliased files'). There is no way for Audacity \
\nto recover these files automatically. \
\n\nIf you choose the first or second option below, \
\nyou can try to find and restore the missing files \
\nto their previous location. \
\n\nNote that for the second option, the waveform \
\nmay not show silence. \
\n\nIf you choose the third option, this will save the \
\nproject in its current state, unless you \"Close \
\nproject immediately\" on further error alerts.");
wxString msg;
msg.Printf(msgA, this->projName, (long long) missingAliasFilesPathHash.size());
const wxChar *buttons[] =
{_("Close project immediately with no changes"),
_("Treat missing audio as silence (this session only)"),
_("Replace missing audio with silence (permanent immediately)."),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Missing Aliased File(s)"), buttons);
}
if (action == 0)
nResult = FSCKstatus_CLOSE_REQ;
else
{
// LL: A progress dialog should probably be used here
BlockHash::iterator iter = missingAliasFilesAUFHash.begin();
while (iter != missingAliasFilesAUFHash.end())
{
// This type cast is safe. We checked that it's an alias block file earlier.
BlockFilePtr b = iter->second.lock();
wxASSERT(b);
if (b) {
auto ab = static_cast< AliasBlockFile * > ( &*b );
if (action == 2)
{
// silence the blockfiles by yanking the filename
// This is done, eventually, in PCMAliasBlockFile::ReadData()
// and ODPCMAliasBlockFile::ReadData, in the stack of b->Recover().
// There, if the mAliasedFileName is bad, it zeroes the data.
wxFileNameWrapper dummy;
dummy.Clear();
ab->ChangeAliasedFileName(std::move(dummy));
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall(
[&] { ab->Recover(); },
[&] (AudacityException*) { action = 1; }
);
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
}
if (action == 1)
// Silence error logging for this block in this session.
ab->SilenceAliasLog();
}
++iter;
}
if ((action == 2) && bAutoRecoverMode)
wxLogWarning(_(" Project check replaced missing aliased file(s) with silence."));
}
}
//
// MISSING ALIAS (.AUF) AliasBlockFiles
//
// Alias summary regeneration must happen after checking missing aliased files.
//
BlockHash missingAUFHash; // missing (.auf) AliasBlockFiles
this->FindMissingAUFs(missingAUFHash);
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAUFHash.empty())
{
// In auto-recover mode, we just recreate the alias files, and do not ask user.
// This makes sure the project is complete next time we open it.
if (bAutoRecoverMode)
action = 0;
else
{
wxString msgA =
_("Project check of \"%s\" folder \
\ndetected %lld missing alias (.auf) blockfile(s). \
\nAudacity can fully regenerate these files \
\nfrom the current audio in the project.");
wxString msg;
msg.Printf(msgA, this->projName, (long long) missingAUFHash.size());
const wxChar *buttons[] = {_("Regenerate alias summary files (safe and recommended)"),
_("Fill in silence for missing display data (this session only)"),
_("Close project immediately with no further changes"),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Missing Alias Summary File(s)"), buttons);
}
if (action == 2)
nResult = FSCKstatus_CLOSE_REQ;
else
{
// LL: A progress dialog should probably be used here
BlockHash::iterator iter = missingAUFHash.begin();
while (iter != missingAUFHash.end())
{
BlockFilePtr b = iter->second.lock();
wxASSERT(b);
if (b) {
if(action==0) {
//regenerate from data
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall(
[&] {
b->Recover();
nResult |= FSCKstatus_CHANGED;
},
[&] (AudacityException*) { action = 1; }
);
}
if (action==1){
// Silence error logging for this block in this session.
b->SilenceLog();
}
}
++iter;
}
if ((action == 0) && bAutoRecoverMode)
wxLogWarning(_(" Project check regenerated missing alias summary file(s)."));
}
}
//
// MISSING (.AU) SimpleBlockFiles
//
BlockHash missingAUHash; // missing data (.au) blockfiles
this->FindMissingAUs(missingAUHash);
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAUHash.empty())
{
// In auto-recover mode, we just always create silent blocks.
// This makes sure the project is complete next time we open it.
if (bAutoRecoverMode)
action = 2;
else
{
wxString msgA =
_("Project check of \"%s\" folder \
\ndetected %lld missing audio data (.au) blockfile(s), \
\nprobably due to a bug, system crash, or accidental \
\ndeletion. There is no way for Audacity to recover \
\nthese missing files automatically. \
\n\nIf you choose the first or second option below, \
\nyou can try to find and restore the missing files \
\nto their previous location. \
\n\nNote that for the second option, the waveform \
\nmay not show silence.");
wxString msg;
msg.Printf(msgA, this->projName, (long long) missingAUHash.size());
const wxChar *buttons[] =
{_("Close project immediately with no further changes"),
_("Treat missing audio as silence (this session only)"),
_("Replace missing audio with silence (permanent immediately)"),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Missing Audio Data Block File(s)"), buttons);
}
if (action == 0)
nResult = FSCKstatus_CLOSE_REQ;
else
{
// LL: A progress dialog should probably be used here
BlockHash::iterator iter = missingAUHash.begin();
while (iter != missingAUHash.end())
{
BlockFilePtr b = iter->second.lock();
wxASSERT(b);
if (b) {
if (action == 2)
{
//regenerate from data
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall(
[&] {
//regenerate with zeroes
b->Recover();
nResult |= FSCKstatus_CHANGED;
},
[&] (AudacityException*) { action = 1; }
);
}
if (action == 1)
b->SilenceLog();
}
++iter;
}
if ((action == 2) && bAutoRecoverMode)
wxLogWarning(_(" Project check replaced missing audio data block file(s) with silence."));
}
}
//
// ORPHAN BLOCKFILES (.au and .auf files that are not in the project.)
//
FilePaths orphanFilePathArray; // orphan .au and .auf files
this->FindOrphanBlockFiles(filePathArray, orphanFilePathArray);
if ((nResult != FSCKstatus_CLOSE_REQ) && !orphanFilePathArray.empty())
{
// In auto-recover mode, leave orphan blockfiles alone.
// They will be deleted when project is saved the first time.
if (bAutoRecoverMode)
{
wxLogWarning(_(" Project check ignored orphan block file(s). They will be deleted when project is saved."));
action = 1;
}
else
{
wxString msgA =
_("Project check of \"%s\" folder \
\nfound %d orphan block file(s). These files are \
\nunused by this project, but might belong to \
other projects. \
\nThey are doing no harm and are small.");
wxString msg;
msg.Printf(msgA, this->projName, (int)orphanFilePathArray.size());
const wxChar *buttons[] =
{_("Continue without deleting; ignore the extra files this session"),
_("Close project immediately with no further changes"),
_("Delete orphan files (permanent immediately)"),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Orphan Block File(s)"), buttons);
}
if (action == 1)
nResult = FSCKstatus_CLOSE_REQ;
// Nothing is done if (action == 0).
else if (action == 2)
{
// FSCKstatus_CHANGED was bogus here.
// The files are deleted, so "Undo Project Repair" could not do anything.
// Plus they affect none of the valid tracks, so incorrect to mark them changed,
// and no need for refresh.
// nResult |= FSCKstatus_CHANGED;
for ( const auto &orphan : orphanFilePathArray )
wxRemoveFile(orphan);
}
}
if ((nResult != FSCKstatus_CLOSE_REQ) && !ODManager::HasLoadedODFlag())
{
// Remove any empty directories.
ProgressDialog pProgress
(_("Progress"),
_("Cleaning up unused directories in project data"));
// nDirCount is for updating pProgress. +1 because we may DELETE dirPath.
int nDirCount = RecursivelyCountSubdirs(dirPath) + 1;
RecursivelyRemoveEmptyDirs(dirPath, nDirCount, &pProgress);
}
// Summarize and flush the log.
if (bForceError ||
!missingAliasFilesAUFHash.empty() ||
!missingAUFHash.empty() ||
!missingAUHash.empty() ||
!orphanFilePathArray.empty())
{
wxLogWarning(_("Project check found file inconsistencies inspecting the loaded project data."));
wxLog::FlushActive(); // Flush is modal and will clear the log (both desired).
// In auto-recover mode, we didn't do any ShowMultiDialog calls above, so put up an alert.
if (bAutoRecoverMode)
::AudacityMessageBox(
_("Project check found file inconsistencies during automatic recovery.\n\nSelect 'Show Log...' in the Help menu to see details."),
_("Warning: Problems in Automatic Recovery"),
wxOK | wxICON_EXCLAMATION);
}
MissingAliasFilesDialog::SetShouldShow(true);
return nResult;
}
void DirManager::FindMissingAliasFiles(
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
BlockHash& missingAliasFilesPathHash) // output: full paths of missing aliased files

View File

@ -19,10 +19,7 @@
class wxFileNameWrapper;
class BlockArray;
class BlockFile;
#define FSCKstatus_CLOSE_REQ 0x1
#define FSCKstatus_CHANGED 0x2
#define FSCKstatus_SAVE_AUP 0x4 // used in combination with FSCKstatus_CHANGED
class ProgressDialog;
using DirHash = std::unordered_map<int, int>;
@ -44,11 +41,39 @@ enum {
class PROFILE_DLL_API DirManager final : public XMLTagHandler {
public:
static int RecursivelyEnumerate(const FilePath &dirPath,
FilePaths& filePathArray, // output: all files in dirPath tree
wxString dirspec,
wxString filespec,
bool bFiles, bool bDirs,
int progress_count = 0,
int progress_bias = 0,
ProgressDialog* progress = nullptr);
static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
FilePaths& filePathArray, // output: all files in dirPath tree
wxString dirspec,
wxString filespec,
bool bFiles, bool bDirs,
int progress_count,
const wxChar* message);
static int RecursivelyCountSubdirs( const FilePath &dirPath );
static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
int nDirCount = 0,
ProgressDialog* pProgress = nullptr);
static void RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
int flags, const wxChar* message = nullptr);
// MM: Construct DirManager
DirManager();
virtual ~DirManager();
size_t NumBlockFiles() const { return mBlockFileHash.size(); }
static void SetTempDir(const wxString &_temp) { globaltemp = _temp; }
class ProjectSetter
@ -152,13 +177,6 @@ class PROFILE_DLL_API DirManager final : public XMLTagHandler {
const wxString &msg,
int flags = 0);
// Check the project for errors and possibly prompt user
// bForceError: Always show log error alert even if no errors are found here.
// Important when you know that there are already errors in the log.
// bAutoRecoverMode: Do not show any option dialogs for how to deal with errors found here.
// Too complicated during auto-recover. Just correct problems the "safest" way.
int ProjectFSCK(const bool bForceError, const bool bAutoRecoverMode);
void FindMissingAliasFiles(
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
BlockHash& missingAliasFilesPathHash); // output: full paths of missing aliased files

View File

@ -116,6 +116,7 @@ scroll information. It also has some status flags.
#include "Mix.h"
#include "NoteTrack.h"
#include "Prefs.h"
#include "ProjectFSCK.h"
#include "Sequence.h"
#include "Snap.h"
#include "Tags.h"
@ -3274,7 +3275,7 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory)
// at this point mFileName != fileName, because when opening a
// recovered file mFileName is faked to point to the original file
// which has been recovered, not the one in the auto-save folder.
GetDirManager()->ProjectFSCK(err, true); // Correct problems in auto-recover mode.
::ProjectFSCK(*GetDirManager(), err, true); // Correct problems in auto-recover mode.
// PushState calls AutoSave(), so no longer need to do so here.
this->PushState(_("Project was recovered"), _("Recover"));
@ -3286,7 +3287,7 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory)
else
{
// This is a regular project, check it and ask user
int status = GetDirManager()->ProjectFSCK(err, false);
int status = ::ProjectFSCK(*GetDirManager(), err, false);
if (status & FSCKstatus_CLOSE_REQ)
{
// Vaughan, 2010-08-23: Note this did not do a real close.
@ -3318,7 +3319,7 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory)
mTrackPanel->Refresh(true);
// Vaughan, 2010-08-20: This was bogus, as all the actions in DirManager::ProjectFSCK
// Vaughan, 2010-08-20: This was bogus, as all the actions in ProjectFSCK
// that return FSCKstatus_CHANGED cannot be undone.
// this->PushState(_("Project checker repaired file"), _("Project Repair"));

View File

@ -0,0 +1,391 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ProjectFSCK.cpp
A function that performs consistency checks on the tree of block files
Paul Licameli split this out of DirManager.cpp
**********************************************************************/
#include "ProjectFSCK.h"
#include <wx/log.h>
#include <wx/string.h>
#include "BlockFile.h"
#include "DirManager.h"
#include "widgets/ErrorDialog.h"
#include "Internat.h"
#include "MemoryX.h"
#include "MissingAliasFileDialog.h"
#include "ondemand/ODManager.h"
#include "widgets/MultiDialog.h"
#include "widgets/ProgressDialog.h"
// Check the BlockFiles against the disk state.
// Missing Blockfile data can be regenerated if possible or replaced with silence.
// Orphan blockfiles can be deleted.
// Note that even BlockFiles not referenced by the current savefile (but locked
// by history) will be reflected in the mBlockFileHash, and that's a
// good thing; this is one reason why we use the hash and not the most
// recent savefile.
int ProjectFSCK(
DirManager &dm, const bool bForceError, const bool bAutoRecoverMode)
{
// In earlier versions of this method, enumerations of errors were
// all done in sequence, then the user was prompted for each type of error.
// The enumerations are now interleaved with prompting, because, for example,
// user choosing to replace missing aliased block files with silence
// needs to put in SilentBlockFiles and DELETE the corresponding auf files,
// so those would then not be cumulated in missingAUFHash.
// We still do the FindX methods outside the conditionals,
// so the log always shows all found errors.
int action; // choice of action for each type of error
int nResult = 0;
if (bForceError && !bAutoRecoverMode)
{
// TODO: Replace with more user friendly error message?
/* i18n-hint: The audacity project file is XML and has 'tags' in it,
rather like html tags <something>some stuff</something>.
This error message is about the tags that hold the sequence information.
The error message is confusing to users in English, and could just say
"Found problems with <sequence> when checking project file." */
wxString msg = _("Project check read faulty Sequence tags.");
const wxChar *buttons[] =
{_("Close project immediately with no changes"),
_("Continue with repairs noted in log, and check for more errors. This will save the project in its current state, unless you \"Close project immediately\" on further error alerts."),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Problems Reading Sequence Tags"), buttons);
if (action == 0)
nResult = FSCKstatus_CLOSE_REQ;
else
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
}
FilePaths filePathArray; // *all* files in the project directory/subdirectories
auto dirPath = ( dm.GetDataFilesDir() );
DirManager::RecursivelyEnumerateWithProgress(
dirPath,
filePathArray, // output: all files in project directory tree
wxEmptyString, // All dirs
wxEmptyString, // All files
true, false,
dm.NumBlockFiles(), // rough guess of how many BlockFiles will be found/processed, for progress
_("Inspecting project file data"));
//
// MISSING ALIASED AUDIO FILES
//
MissingAliasFilesDialog::SetShouldShow(false);
BlockHash missingAliasFilesAUFHash; // (.auf) AliasBlockFiles whose aliased files are missing
BlockHash missingAliasFilesPathHash; // full paths of missing aliased files
dm.FindMissingAliasFiles(missingAliasFilesAUFHash, missingAliasFilesPathHash);
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAliasFilesAUFHash.empty())
{
// In auto-recover mode, we always create silent blocks, and do not ask user.
// This makes sure the project is complete next time we open it.
if (bAutoRecoverMode)
action = 2;
else
{
wxString msgA =
_("Project check of \"%s\" folder \
\ndetected %lld missing external audio file(s) \
\n('aliased files'). There is no way for Audacity \
\nto recover these files automatically. \
\n\nIf you choose the first or second option below, \
\nyou can try to find and restore the missing files \
\nto their previous location. \
\n\nNote that for the second option, the waveform \
\nmay not show silence. \
\n\nIf you choose the third option, this will save the \
\nproject in its current state, unless you \"Close \
\nproject immediately\" on further error alerts.");
wxString msg;
msg.Printf(msgA, dm.GetProjectName(), (long long) missingAliasFilesPathHash.size());
const wxChar *buttons[] =
{_("Close project immediately with no changes"),
_("Treat missing audio as silence (this session only)"),
_("Replace missing audio with silence (permanent immediately)."),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Missing Aliased File(s)"), buttons);
}
if (action == 0)
nResult = FSCKstatus_CLOSE_REQ;
else
{
// LL: A progress dialog should probably be used here
BlockHash::iterator iter = missingAliasFilesAUFHash.begin();
while (iter != missingAliasFilesAUFHash.end())
{
// This type cast is safe. We checked that it's an alias block file earlier.
BlockFilePtr b = iter->second.lock();
wxASSERT(b);
if (b) {
auto ab = static_cast< AliasBlockFile * > ( &*b );
if (action == 2)
{
// silence the blockfiles by yanking the filename
// This is done, eventually, in PCMAliasBlockFile::ReadData()
// and ODPCMAliasBlockFile::ReadData, in the stack of b->Recover().
// There, if the mAliasedFileName is bad, it zeroes the data.
wxFileNameWrapper dummy;
dummy.Clear();
ab->ChangeAliasedFileName(std::move(dummy));
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall(
[&] { ab->Recover(); },
[&] (AudacityException*) { action = 1; }
);
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
}
if (action == 1)
// Silence error logging for this block in this session.
ab->SilenceAliasLog();
}
++iter;
}
if ((action == 2) && bAutoRecoverMode)
wxLogWarning(_(" Project check replaced missing aliased file(s) with silence."));
}
}
//
// MISSING ALIAS (.AUF) AliasBlockFiles
//
// Alias summary regeneration must happen after checking missing aliased files.
//
BlockHash missingAUFHash; // missing (.auf) AliasBlockFiles
dm.FindMissingAUFs(missingAUFHash);
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAUFHash.empty())
{
// In auto-recover mode, we just recreate the alias files, and do not ask user.
// This makes sure the project is complete next time we open it.
if (bAutoRecoverMode)
action = 0;
else
{
wxString msgA =
_("Project check of \"%s\" folder \
\ndetected %lld missing alias (.auf) blockfile(s). \
\nAudacity can fully regenerate these files \
\nfrom the current audio in the project.");
wxString msg;
msg.Printf(msgA, dm.GetProjectName(), (long long) missingAUFHash.size());
const wxChar *buttons[] = {_("Regenerate alias summary files (safe and recommended)"),
_("Fill in silence for missing display data (this session only)"),
_("Close project immediately with no further changes"),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Missing Alias Summary File(s)"), buttons);
}
if (action == 2)
nResult = FSCKstatus_CLOSE_REQ;
else
{
// LL: A progress dialog should probably be used here
BlockHash::iterator iter = missingAUFHash.begin();
while (iter != missingAUFHash.end())
{
BlockFilePtr b = iter->second.lock();
wxASSERT(b);
if (b) {
if(action==0) {
//regenerate from data
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall(
[&] {
b->Recover();
nResult |= FSCKstatus_CHANGED;
},
[&] (AudacityException*) { action = 1; }
);
}
if (action==1){
// Silence error logging for this block in this session.
b->SilenceLog();
}
}
++iter;
}
if ((action == 0) && bAutoRecoverMode)
wxLogWarning(_(" Project check regenerated missing alias summary file(s)."));
}
}
//
// MISSING (.AU) SimpleBlockFiles
//
BlockHash missingAUHash; // missing data (.au) blockfiles
dm.FindMissingAUs(missingAUHash);
if ((nResult != FSCKstatus_CLOSE_REQ) && !missingAUHash.empty())
{
// In auto-recover mode, we just always create silent blocks.
// This makes sure the project is complete next time we open it.
if (bAutoRecoverMode)
action = 2;
else
{
wxString msgA =
_("Project check of \"%s\" folder \
\ndetected %lld missing audio data (.au) blockfile(s), \
\nprobably due to a bug, system crash, or accidental \
\ndeletion. There is no way for Audacity to recover \
\nthese missing files automatically. \
\n\nIf you choose the first or second option below, \
\nyou can try to find and restore the missing files \
\nto their previous location. \
\n\nNote that for the second option, the waveform \
\nmay not show silence.");
wxString msg;
msg.Printf(msgA, dm.GetProjectName(), (long long) missingAUHash.size());
const wxChar *buttons[] =
{_("Close project immediately with no further changes"),
_("Treat missing audio as silence (this session only)"),
_("Replace missing audio with silence (permanent immediately)"),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Missing Audio Data Block File(s)"), buttons);
}
if (action == 0)
nResult = FSCKstatus_CLOSE_REQ;
else
{
// LL: A progress dialog should probably be used here
BlockHash::iterator iter = missingAUHash.begin();
while (iter != missingAUHash.end())
{
BlockFilePtr b = iter->second.lock();
wxASSERT(b);
if (b) {
if (action == 2)
{
//regenerate from data
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall(
[&] {
//regenerate with zeroes
b->Recover();
nResult |= FSCKstatus_CHANGED;
},
[&] (AudacityException*) { action = 1; }
);
}
if (action == 1)
b->SilenceLog();
}
++iter;
}
if ((action == 2) && bAutoRecoverMode)
wxLogWarning(_(" Project check replaced missing audio data block file(s) with silence."));
}
}
//
// ORPHAN BLOCKFILES (.au and .auf files that are not in the project.)
//
FilePaths orphanFilePathArray; // orphan .au and .auf files
dm.FindOrphanBlockFiles(filePathArray, orphanFilePathArray);
if ((nResult != FSCKstatus_CLOSE_REQ) && !orphanFilePathArray.empty())
{
// In auto-recover mode, leave orphan blockfiles alone.
// They will be deleted when project is saved the first time.
if (bAutoRecoverMode)
{
wxLogWarning(_(" Project check ignored orphan block file(s). They will be deleted when project is saved."));
action = 1;
}
else
{
wxString msgA =
_("Project check of \"%s\" folder \
\nfound %d orphan block file(s). These files are \
\nunused by this project, but might belong to \
other projects. \
\nThey are doing no harm and are small.");
wxString msg;
msg.Printf(msgA, dm.GetProjectName(), (int)orphanFilePathArray.size());
const wxChar *buttons[] =
{_("Continue without deleting; ignore the extra files this session"),
_("Close project immediately with no further changes"),
_("Delete orphan files (permanent immediately)"),
NULL};
wxLog::FlushActive(); // MultiDialog has "Show Log..." button, so make sure log is current.
action = ShowMultiDialog(msg, _("Warning - Orphan Block File(s)"), buttons);
}
if (action == 1)
nResult = FSCKstatus_CLOSE_REQ;
// Nothing is done if (action == 0).
else if (action == 2)
{
// FSCKstatus_CHANGED was bogus here.
// The files are deleted, so "Undo Project Repair" could not do anything.
// Plus they affect none of the valid tracks, so incorrect to mark them changed,
// and no need for refresh.
// nResult |= FSCKstatus_CHANGED;
for ( const auto &orphan : orphanFilePathArray )
wxRemoveFile(orphan);
}
}
if ((nResult != FSCKstatus_CLOSE_REQ) && !ODManager::HasLoadedODFlag())
{
// Remove any empty directories.
ProgressDialog pProgress
(_("Progress"),
_("Cleaning up unused directories in project data"));
// nDirCount is for updating pProgress. +1 because we may DELETE dirPath.
int nDirCount = DirManager::RecursivelyCountSubdirs(dirPath) + 1;
DirManager::RecursivelyRemoveEmptyDirs(dirPath, nDirCount, &pProgress);
}
// Summarize and flush the log.
if (bForceError ||
!missingAliasFilesAUFHash.empty() ||
!missingAUFHash.empty() ||
!missingAUHash.empty() ||
!orphanFilePathArray.empty())
{
wxLogWarning(_("Project check found file inconsistencies inspecting the loaded project data."));
wxLog::FlushActive(); // Flush is modal and will clear the log (both desired).
// In auto-recover mode, we didn't do any ShowMultiDialog calls above, so put up an alert.
if (bAutoRecoverMode)
::AudacityMessageBox(
_("Project check found file inconsistencies during automatic recovery.\n\nSelect 'Show Log...' in the Help menu to see details."),
_("Warning: Problems in Automatic Recovery"),
wxOK | wxICON_EXCLAMATION);
}
MissingAliasFilesDialog::SetShouldShow(true);
return nResult;
}

View File

@ -0,0 +1,32 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ProjectFSCK.h
A function that performs consistency checks on the tree of block files
Paul Licameli split this out of DirManager.h
**********************************************************************/
#ifndef __AUDACITY_PROJECT_FSCK__
#define __AUDACITY_PROJECT_FSCK__
class DirManager;
enum : unsigned {
FSCKstatus_CLOSE_REQ = 0x1,
FSCKstatus_CHANGED = 0x2,
FSCKstatus_SAVE_AUP = 0x4, // used in combination with FSCKstatus_CHANGED
};
// Check the project for errors and possibly prompt user
// bForceError: Always show log error alert even if no errors are found here.
// Important when you know that there are already errors in the log.
// bAutoRecoverMode: Do not show any option dialogs for how to deal with errors found here.
// Too complicated during auto-recover. Just correct problems the "safest" way.
int ProjectFSCK(
DirManager &dm, const bool bForceError, const bool bAutoRecoverMode);
#endif

View File

@ -82,7 +82,7 @@ void LegacyAliasBlockFile::SaveXML(XMLWriter &xmlFile)
// BuildFromXML methods should always return a BlockFile, not NULL,
// even if the result is flawed (e.g., refers to nonexistent file),
// as testing will be done in DirManager::ProjectFSCK().
// as testing will be done in ProjectFSCK().
BlockFilePtr LegacyAliasBlockFile::BuildFromXML(const FilePath &projDir, const wxChar **attrs)
{
wxFileNameWrapper summaryFileName;

View File

@ -218,7 +218,7 @@ void LegacyBlockFile::SaveXML(XMLWriter &xmlFile)
// BuildFromXML methods should always return a BlockFile, not NULL,
// even if the result is flawed (e.g., refers to nonexistent file),
// as testing will be done in DirManager::ProjectFSCK().
// as testing will be done in ProjectFSCK().
/// static
BlockFilePtr LegacyBlockFile::BuildFromXML(const FilePath &projDir, const wxChar **attrs,
size_t len, sampleFormat format)

View File

@ -237,7 +237,7 @@ void ODDecodeBlockFile::SaveXML(XMLWriter &xmlFile)
/// Also schedules the ODDecodeBlockFile for OD loading.
// BuildFromXML methods should always return a BlockFile, not NULL,
// even if the result is flawed (e.g., refers to nonexistent file),
// as testing will be done in DirManager::ProjectFSCK().
// as testing will be done in ProjectFSCK().
BlockFilePtr ODDecodeBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
{
wxFileNameWrapper summaryFileName;

View File

@ -271,7 +271,7 @@ void ODPCMAliasBlockFile::SaveXML(XMLWriter &xmlFile)
/// Does not schedule the ODPCMAliasBlockFile for OD loading. Client code must do this.
// BuildFromXML methods should always return a BlockFile, not NULL,
// even if the result is flawed (e.g., refers to nonexistent file),
// as testing will be done in DirManager::ProjectFSCK().
// as testing will be done in ProjectFSCK().
BlockFilePtr ODPCMAliasBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
{
wxFileNameWrapper summaryFileName;

View File

@ -114,7 +114,7 @@ void PCMAliasBlockFile::SaveXML(XMLWriter &xmlFile)
// BuildFromXML methods should always return a BlockFile, not NULL,
// even if the result is flawed (e.g., refers to nonexistent file),
// as testing will be done in DirManager::ProjectFSCK().
// as testing will be done in ProjectFSCK().
BlockFilePtr PCMAliasBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
{
wxFileNameWrapper summaryFileName;

View File

@ -53,7 +53,7 @@ void SilentBlockFile::SaveXML(XMLWriter &xmlFile)
// BuildFromXML methods should always return a BlockFile, not NULL,
// even if the result is flawed (e.g., refers to nonexistent file),
// as testing will be done in DirManager::ProjectFSCK().
// as testing will be done in ProjectFSCK().
/// static
BlockFilePtr SilentBlockFile::BuildFromXML(DirManager & WXUNUSED(dm), const wxChar **attrs)
{

View File

@ -433,7 +433,7 @@ void SimpleBlockFile::SaveXML(XMLWriter &xmlFile)
// BuildFromXML methods should always return a BlockFile, not NULL,
// even if the result is flawed (e.g., refers to nonexistent file),
// as testing will be done in DirManager::ProjectFSCK().
// as testing will be done in ProjectFSCK().
/// static
BlockFilePtr SimpleBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
{