mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-15 15:49:36 +02:00
Move ProjectFSCK out of DirManager...
... which makes DirManager.cpp not depend on MissingAliasFileDialog.cpp
This commit is contained in:
parent
e5c7e5a21f
commit
2faa24c96b
@ -124,7 +124,7 @@ void FindDependencies(AudacityProject *project,
|
|||||||
auto aliasBlockFile = static_cast<AliasBlockFile*>( &*f );
|
auto aliasBlockFile = static_cast<AliasBlockFile*>( &*f );
|
||||||
const wxFileName &fileName = aliasBlockFile->GetAliasedFileName();
|
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.
|
// "Replace missing audio with silence", the code there puts in an empty wxFileName.
|
||||||
// Don't count those in dependencies.
|
// Don't count those in dependencies.
|
||||||
if (!fileName.IsOk())
|
if (!fileName.IsOk())
|
||||||
|
@ -93,12 +93,10 @@
|
|||||||
#include "blockfile/ODPCMAliasBlockFile.h"
|
#include "blockfile/ODPCMAliasBlockFile.h"
|
||||||
#include "blockfile/ODDecodeBlockFile.h"
|
#include "blockfile/ODDecodeBlockFile.h"
|
||||||
#include "InconsistencyException.h"
|
#include "InconsistencyException.h"
|
||||||
#include "MissingAliasFileDialog.h"
|
|
||||||
#include "Project.h"
|
#include "Project.h"
|
||||||
#include "Prefs.h"
|
#include "Prefs.h"
|
||||||
#include "Sequence.h"
|
#include "Sequence.h"
|
||||||
#include "widgets/Warning.h"
|
#include "widgets/Warning.h"
|
||||||
#include "widgets/MultiDialog.h"
|
|
||||||
#include "widgets/ErrorDialog.h"
|
#include "widgets/ErrorDialog.h"
|
||||||
#include "widgets/ProgressDialog.h"
|
#include "widgets/ProgressDialog.h"
|
||||||
|
|
||||||
@ -152,14 +150,14 @@ wxMemorySize GetFreeMemory()
|
|||||||
// JKC: Using flag wxDIR_NO_FOLLOW to NOT follow symbolic links.
|
// JKC: Using flag wxDIR_NO_FOLLOW to NOT follow symbolic links.
|
||||||
// Directories and files inside a project should never be symbolic
|
// Directories and files inside a project should never be symbolic
|
||||||
// links, so if we find one, do not follow it.
|
// 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
|
FilePaths& filePathArray, // output: all files in dirPath tree
|
||||||
wxString dirspec,
|
wxString dirspec,
|
||||||
wxString filespec,
|
wxString filespec,
|
||||||
bool bFiles, bool bDirs,
|
bool bFiles, bool bDirs,
|
||||||
int progress_count = 0,
|
int progress_count,
|
||||||
int progress_bias = 0,
|
int progress_bias,
|
||||||
ProgressDialog* progress = NULL)
|
ProgressDialog* progress)
|
||||||
{
|
{
|
||||||
int count=0;
|
int count=0;
|
||||||
bool cont;
|
bool cont;
|
||||||
@ -206,7 +204,7 @@ static int RecursivelyEnumerate(const FilePath &dirPath,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
|
int DirManager::RecursivelyEnumerateWithProgress(const FilePath &dirPath,
|
||||||
FilePaths& filePathArray, // output: all files in dirPath tree
|
FilePaths& filePathArray, // output: all files in dirPath tree
|
||||||
wxString dirspec,
|
wxString dirspec,
|
||||||
wxString filespec,
|
wxString filespec,
|
||||||
@ -228,7 +226,7 @@ static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int RecursivelyCountSubdirs( const FilePath &dirPath )
|
int DirManager::RecursivelyCountSubdirs( const FilePath &dirPath )
|
||||||
{
|
{
|
||||||
bool bContinue;
|
bool bContinue;
|
||||||
int nCount = 0;
|
int nCount = 0;
|
||||||
@ -248,9 +246,9 @@ static int RecursivelyCountSubdirs( const FilePath &dirPath )
|
|||||||
return nCount;
|
return nCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
|
int DirManager::RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
|
||||||
int nDirCount = 0,
|
int nDirCount,
|
||||||
ProgressDialog* pProgress = NULL)
|
ProgressDialog* pProgress)
|
||||||
{
|
{
|
||||||
bool bContinue;
|
bool bContinue;
|
||||||
wxDir dir(dirPath);
|
wxDir dir(dirPath);
|
||||||
@ -293,8 +291,8 @@ static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
|
|||||||
return nCount;
|
return nCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
|
void DirManager::RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
|
||||||
int flags, const wxChar* message = NULL)
|
int flags, const wxChar* message)
|
||||||
{
|
{
|
||||||
bool bFiles= (flags & kCleanFiles) != 0;
|
bool bFiles= (flags & kCleanFiles) != 0;
|
||||||
bool bDirs = (flags & kCleanDirs) != 0;
|
bool bDirs = (flags & kCleanDirs) != 0;
|
||||||
@ -1645,370 +1643,6 @@ bool DirManager::EnsureSafeFilename(const wxFileName &fName)
|
|||||||
return true;
|
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(
|
void DirManager::FindMissingAliasFiles(
|
||||||
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
|
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
|
||||||
BlockHash& missingAliasFilesPathHash) // output: full paths of missing aliased files
|
BlockHash& missingAliasFilesPathHash) // output: full paths of missing aliased files
|
||||||
|
@ -19,10 +19,7 @@
|
|||||||
class wxFileNameWrapper;
|
class wxFileNameWrapper;
|
||||||
class BlockArray;
|
class BlockArray;
|
||||||
class BlockFile;
|
class BlockFile;
|
||||||
|
class ProgressDialog;
|
||||||
#define FSCKstatus_CLOSE_REQ 0x1
|
|
||||||
#define FSCKstatus_CHANGED 0x2
|
|
||||||
#define FSCKstatus_SAVE_AUP 0x4 // used in combination with FSCKstatus_CHANGED
|
|
||||||
|
|
||||||
using DirHash = std::unordered_map<int, int>;
|
using DirHash = std::unordered_map<int, int>;
|
||||||
|
|
||||||
@ -44,11 +41,39 @@ enum {
|
|||||||
class PROFILE_DLL_API DirManager final : public XMLTagHandler {
|
class PROFILE_DLL_API DirManager final : public XMLTagHandler {
|
||||||
public:
|
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
|
// MM: Construct DirManager
|
||||||
DirManager();
|
DirManager();
|
||||||
|
|
||||||
virtual ~DirManager();
|
virtual ~DirManager();
|
||||||
|
|
||||||
|
size_t NumBlockFiles() const { return mBlockFileHash.size(); }
|
||||||
|
|
||||||
static void SetTempDir(const wxString &_temp) { globaltemp = _temp; }
|
static void SetTempDir(const wxString &_temp) { globaltemp = _temp; }
|
||||||
|
|
||||||
class ProjectSetter
|
class ProjectSetter
|
||||||
@ -152,13 +177,6 @@ class PROFILE_DLL_API DirManager final : public XMLTagHandler {
|
|||||||
const wxString &msg,
|
const wxString &msg,
|
||||||
int flags = 0);
|
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(
|
void FindMissingAliasFiles(
|
||||||
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
|
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
|
||||||
BlockHash& missingAliasFilesPathHash); // output: full paths of missing aliased files
|
BlockHash& missingAliasFilesPathHash); // output: full paths of missing aliased files
|
||||||
|
@ -116,6 +116,7 @@ scroll information. It also has some status flags.
|
|||||||
#include "Mix.h"
|
#include "Mix.h"
|
||||||
#include "NoteTrack.h"
|
#include "NoteTrack.h"
|
||||||
#include "Prefs.h"
|
#include "Prefs.h"
|
||||||
|
#include "ProjectFSCK.h"
|
||||||
#include "Sequence.h"
|
#include "Sequence.h"
|
||||||
#include "Snap.h"
|
#include "Snap.h"
|
||||||
#include "Tags.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
|
// at this point mFileName != fileName, because when opening a
|
||||||
// recovered file mFileName is faked to point to the original file
|
// recovered file mFileName is faked to point to the original file
|
||||||
// which has been recovered, not the one in the auto-save folder.
|
// 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.
|
// PushState calls AutoSave(), so no longer need to do so here.
|
||||||
this->PushState(_("Project was recovered"), _("Recover"));
|
this->PushState(_("Project was recovered"), _("Recover"));
|
||||||
@ -3286,7 +3287,7 @@ void AudacityProject::OpenFile(const FilePath &fileNameArg, bool addtohistory)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This is a regular project, check it and ask user
|
// 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)
|
if (status & FSCKstatus_CLOSE_REQ)
|
||||||
{
|
{
|
||||||
// Vaughan, 2010-08-23: Note this did not do a real close.
|
// 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);
|
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.
|
// that return FSCKstatus_CHANGED cannot be undone.
|
||||||
// this->PushState(_("Project checker repaired file"), _("Project Repair"));
|
// this->PushState(_("Project checker repaired file"), _("Project Repair"));
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -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
|
@ -82,7 +82,7 @@ void LegacyAliasBlockFile::SaveXML(XMLWriter &xmlFile)
|
|||||||
|
|
||||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
// 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)
|
BlockFilePtr LegacyAliasBlockFile::BuildFromXML(const FilePath &projDir, const wxChar **attrs)
|
||||||
{
|
{
|
||||||
wxFileNameWrapper summaryFileName;
|
wxFileNameWrapper summaryFileName;
|
||||||
|
@ -218,7 +218,7 @@ void LegacyBlockFile::SaveXML(XMLWriter &xmlFile)
|
|||||||
|
|
||||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
// 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
|
/// static
|
||||||
BlockFilePtr LegacyBlockFile::BuildFromXML(const FilePath &projDir, const wxChar **attrs,
|
BlockFilePtr LegacyBlockFile::BuildFromXML(const FilePath &projDir, const wxChar **attrs,
|
||||||
size_t len, sampleFormat format)
|
size_t len, sampleFormat format)
|
||||||
|
@ -237,7 +237,7 @@ void ODDecodeBlockFile::SaveXML(XMLWriter &xmlFile)
|
|||||||
/// Also schedules the ODDecodeBlockFile for OD loading.
|
/// Also schedules the ODDecodeBlockFile for OD loading.
|
||||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
// 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)
|
BlockFilePtr ODDecodeBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
||||||
{
|
{
|
||||||
wxFileNameWrapper summaryFileName;
|
wxFileNameWrapper summaryFileName;
|
||||||
|
@ -271,7 +271,7 @@ void ODPCMAliasBlockFile::SaveXML(XMLWriter &xmlFile)
|
|||||||
/// Does not schedule the ODPCMAliasBlockFile for OD loading. Client code must do this.
|
/// Does not schedule the ODPCMAliasBlockFile for OD loading. Client code must do this.
|
||||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
// 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)
|
BlockFilePtr ODPCMAliasBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
||||||
{
|
{
|
||||||
wxFileNameWrapper summaryFileName;
|
wxFileNameWrapper summaryFileName;
|
||||||
|
@ -114,7 +114,7 @@ void PCMAliasBlockFile::SaveXML(XMLWriter &xmlFile)
|
|||||||
|
|
||||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
// 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)
|
BlockFilePtr PCMAliasBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
||||||
{
|
{
|
||||||
wxFileNameWrapper summaryFileName;
|
wxFileNameWrapper summaryFileName;
|
||||||
|
@ -53,7 +53,7 @@ void SilentBlockFile::SaveXML(XMLWriter &xmlFile)
|
|||||||
|
|
||||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
// 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
|
/// static
|
||||||
BlockFilePtr SilentBlockFile::BuildFromXML(DirManager & WXUNUSED(dm), const wxChar **attrs)
|
BlockFilePtr SilentBlockFile::BuildFromXML(DirManager & WXUNUSED(dm), const wxChar **attrs)
|
||||||
{
|
{
|
||||||
|
@ -433,7 +433,7 @@ void SimpleBlockFile::SaveXML(XMLWriter &xmlFile)
|
|||||||
|
|
||||||
// BuildFromXML methods should always return a BlockFile, not NULL,
|
// BuildFromXML methods should always return a BlockFile, not NULL,
|
||||||
// even if the result is flawed (e.g., refers to nonexistent file),
|
// 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
|
/// static
|
||||||
BlockFilePtr SimpleBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
BlockFilePtr SimpleBlockFile::BuildFromXML(DirManager &dm, const wxChar **attrs)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user