11namespace tracktion {
inline namespace engine
19TemporaryFileManager::~TemporaryFileManager()
28const juce::File& TemporaryFileManager::getTempDirectory()
const
33bool TemporaryFileManager::setTempDirectory (
const juce::File& newFile)
35 auto defaultDir = getDefaultTempFolder (engine);
37 if (newFile == defaultDir)
39 ressetToDefaultLocation();
44 auto parent = defaultDir.getParentDirectory();
55 return wasTempFolderSuccessfullyCreated();
58void TemporaryFileManager::ressetToDefaultLocation()
60 tempDir = getDefaultTempFolder (engine);
64void TemporaryFileManager::updateDir()
66 auto defaultDir = getDefaultTempFolder (engine);
67 auto userFolder = engine.
getPropertyStorage().getProperty (SettingID::tempDirectory).toString().trim();
69 if (userFolder.isEmpty())
70 ressetToDefaultLocation();
74 if (! wasTempFolderSuccessfullyCreated())
82bool TemporaryFileManager::wasTempFolderSuccessfullyCreated()
const
88bool TemporaryFileManager::isDiskSpaceDangerouslyLow()
const
94int64_t TemporaryFileManager::getMaxSpaceAllowedForTempFiles()
const
96 int64_t minAbsoluteSize = 1024 * 1024 * 750;
99 return std::min (minProportionOfDisk, minAbsoluteSize);
102int TemporaryFileManager::getMaxNumTempFiles()
const
107static bool shouldDeleteTempFile (
const juce::File& f,
bool spaceIsShort)
111 if (fileName.startsWith (
"preview_"))
114 if (fileName.startsWith (
"temp_"))
119 return daysOld > 60.0 || (spaceIsShort && daysOld > 1.0);
124 auto& pm = engine.getProjectManager();
127 for (
auto& p : pm.getAllProjects (pm.folders))
128 for (auto& itemID : p->getAllProjectItemIDs())
129 ids.add (itemID.toStringSuitableForFilename());
131 for (
int i = files.
size(); --i >= 0;)
133 auto fileName = files.
getUnchecked (i).getFileNameWithoutExtension();
135 if (! fileName.startsWith (
"preview_"))
138 auto itemID = fileName.fromFirstOccurrenceOf (
"preview_",
false,
false);
140 if (itemID.isNotEmpty() && ! ids.
contains (itemID))
145void TemporaryFileManager::cleanUp()
148 TRACKTION_LOG (
"Cleaning up temp files..");
152 deleteEditPreviewsNotInUse (engine, tempFiles);
156 for (
auto& f : tempFiles)
157 totalBytes += f.getSize();
159 std::sort (tempFiles.begin(), tempFiles.end(),
162 return first.getLastAccessTime().toMilliseconds() < second.getLastAccessTime().toMilliseconds();
165 auto numFiles = tempFiles.size();
166 auto maxNumFiles = getMaxNumTempFiles();
167 auto maxSizeToKeep = getMaxSpaceAllowedForTempFiles();
169 for (
auto& f : tempFiles)
171 if (shouldDeleteTempFile (f, totalBytes > maxSizeToKeep || numFiles > maxNumFiles))
179 for (
auto entry :
juce::RangedDirectoryIterator (getTempDirectory(), false,
"edit_*",
juce::File::findDirectories))
180 if (entry.getFile().getNumberOfChildFiles (
juce::File::findFilesAndDirectories) == 0)
181 entry.getFile().deleteRecursively();
196juce::File TemporaryFileManager::getThumbnailsFolder()
const
202static juce::String getClipProxyPrefix() {
return "clip_"; }
203static juce::String getFileProxyPrefix() {
return "proxy_"; }
204static juce::String getDeviceFreezePrefix (Edit& edit) {
return "freeze_" + edit.getProjectItemID().toStringSuitableForFilename() +
"_"; }
205static juce::String getTrackFreezePrefix() {
return "trackFreeze_"; }
208static AudioFile getCachedEditFile (Edit& edit,
const juce::String& prefix, HashCode hash)
210 return AudioFile (edit.engine, edit.getTempDirectory (
true).getChildFile (prefix +
juce::String::toHexString (hash) +
".wav"));
213static AudioFile getCachedClipFileWithPrefix (
const AudioClipBase& clip,
const juce::String& prefix, HashCode hash)
215 return getCachedEditFile (clip.edit, prefix +
"0_" + clip.itemID.toString() +
"_", hash);
218AudioFile TemporaryFileManager::getFileForCachedClipRender (
const AudioClipBase& clip, HashCode hash)
220 return getCachedClipFileWithPrefix (clip, getClipProxyPrefix(), hash);
223AudioFile TemporaryFileManager::getFileForCachedCompRender (
const AudioClipBase& clip, HashCode takeHash)
225 return getCachedClipFileWithPrefix (clip, getCompPrefix(), takeHash);
228AudioFile TemporaryFileManager::getFileForCachedFileRender (Edit& edit, HashCode hash)
230 return getCachedEditFile (edit, getFileProxyPrefix(), hash);
233juce::File TemporaryFileManager::getFreezeFileForDevice (Edit& edit, OutputDevice& device)
235 return edit.getTempDirectory (
true)
239juce::String TemporaryFileManager::getDeviceIDFromFreezeFile (Edit& edit,
const juce::File& deviceFreezeFile)
241 const auto fileName = deviceFreezeFile.
getFileName();
242 jassert (fileName.startsWith (getDeviceFreezePrefix (edit)));
243 jassert (fileName.endsWith (
".freeze"));
245 return fileName.fromLastOccurrenceOf (getDeviceFreezePrefix (edit),
false,
false)
246 .upToFirstOccurrenceOf (
".",
false,
false);
249juce::File TemporaryFileManager::getFreezeFileForTrack (
const AudioTrack& track)
251 return track.edit.getTempDirectory (
true)
252 .getChildFile (getTrackFreezePrefix() +
"0_" + track.itemID.
toString() +
".freeze");
257 return edit.getTempDirectory (
false)
261static ProjectItemID getProjectItemIDFromFilename (
const juce::String& name)
265 if (tokens.size() <= 1)
268 return ProjectItemID (tokens[0] +
"_" + tokens[1]);
271void TemporaryFileManager::purgeOrphanEditTempFolders (ProjectManager& pm)
277 for (
auto entry :
juce::RangedDirectoryIterator (getTempDirectory(), false,
"edit_*",
juce::File::findDirectories))
279 auto itemID = getProjectItemIDFromFilename (entry.getFile().getFileName());
281 if (itemID.isValid())
283 if (pm.getProjectItem (itemID) ==
nullptr)
285 filesToDelete.add (entry.getFile());
287 auto pp = pm.getProject (itemID.getProjectID());
289 if (itemID.getProjectID() == 0)
290 reasonsForDeletion.
add (
"Invalid project ID");
291 else if (pp ==
nullptr)
292 reasonsForDeletion.
add (
"Can't find project");
293 else if (pp->getProjectItemForID (itemID) ==
nullptr)
294 reasonsForDeletion.
add (
"Can't find source media");
296 reasonsForDeletion.
add (
"Unknown");
301 for (
int i = filesToDelete.size(); --i >= 0;)
303 TRACKTION_LOG (
"Purging edit folder: " + filesToDelete.getReference (i).getFileName() +
" - " + reasonsForDeletion[i]);
304 filesToDelete.getReference (i).deleteRecursively();
308static EditItemID getEditItemIDFromFilename (
const juce::String& name)
312 if (tokens.isEmpty())
317 return EditItemID::fromVar (tokens[0] + tokens[1]);
320void TemporaryFileManager::purgeOrphanFreezeAndProxyFiles (Edit& edit)
325 for (
auto entry :
juce::RangedDirectoryIterator (edit.getTempDirectory (false), false,
"*"))
327 auto name = entry.getFile().getFileName();
328 auto itemID = getEditItemIDFromFilename (name);
330 if (itemID.isValid())
332 if (name.startsWith (getClipProxyPrefix())
333 || name.startsWith (getCompPrefix()))
337 if (
auto acb =
dynamic_cast<AudioClipBase*
> (clip))
339 if (! acb->isUsingFile (AudioFile (edit.engine, entry.getFile())))
340 filesToDelete.add (entry.getFile());
342 else if (clip ==
nullptr)
344 filesToDelete.add (entry.getFile());
347 else if (name.startsWith (getFileProxyPrefix()))
351 else if (name.startsWith (getTrackFreezePrefix()))
355 filesToDelete.add (entry.getFile());
360 if (! edit.areAnyClipsUsingFile (AudioFile (edit.engine, entry.getFile())))
361 filesToDelete.add (entry.getFile());
365 for (
auto& f : filesToDelete)
368 AudioFile (edit.engine, f).deleteFile();
ElementType getUnchecked(int index) const
int size() const noexcept
ElementType removeAndReturn(int indexToRemove)
Array< File > findChildFiles(int whatToLookFor, bool searchRecursively, const String &wildCardPattern="*", FollowSymlinks followSymlinks=FollowSymlinks::yes) const
int64 getBytesFreeOnVolume() const
bool hasWriteAccess() const
const String & getFullPathName() const noexcept
String getFileName() const
File getChildFile(StringRef relativeOrAbsolutePath) const
File getSiblingFile(StringRef siblingFileName) const
File getNonexistentSibling(bool putNumbersInBrackets=true) const
String getRelativePathFrom(const File &directoryToBeRelativeTo) const
File withFileExtension(StringRef newExtension) const
bool isAChildOf(const File &potentialParentDirectory) const
Time getLastAccessTime() const
Result createDirectory() const
const String & toString() const noexcept
static Random & getSystemRandom() noexcept
bool contains(StringRef stringToLookFor, bool ignoreCase=false) const
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
void add(String stringToAdd)
static String toHexString(IntegerType number)
static Time JUCE_CALLTYPE getCurrentTime() noexcept
The Engine is the central class for all tracktion sessions.
PropertyStorage & getPropertyStorage() const
Returns the PropertyStorage user settings customisable XML file.
static juce::StringRef getFileRenderPrefix()
Returns the prefix used for render files.
TemporaryFileManager(Engine &)
You shouldn't have to ever create your own instance of this class - the Engine itself has a Temporary...
@ individualFreeze
Freezes a track in to a single audio file.
Clip * findClipForID(ClipOwner &co, EditItemID id)
Returns a clip with the given ID if the ClipOwner contains it.
AudioTrack * findAudioTrackForID(const Edit &edit, EditItemID id)
Returns the AudioTrack with a given ID if contained in the Edit.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.