gopro-ingester/source/ingest.d

102 lines
3.3 KiB
D

module ingest;
import std.file;
import std.stdio;
import std.path;
import std.algorithm;
import std.string;
import std.typecons;
import filesizes;
import progress;
private struct IngestData {
DirEntry[] filesToCopy;
ulong totalFileSize() {
return filesToCopy.map!(f => f.size).sum;
}
size_t fileCount() {
return filesToCopy.length;
}
}
/**
* Determines if we should copy a given file from the media card to the local
* directory.
* Params:
* entry = The entry for the file at its source.
* targetFile = The place to copy the file to.
* force = Whether the force flag has been set.
* Returns: True if we should copy the file, or false otherwise.
*/
private bool shouldCopyFile(DirEntry entry, string targetFile, bool force) {
return entry.isFile() &&
(entry.name.endsWith(".MP4") || entry.name.endsWith(".WAV")) &&
(!exists(targetFile) || getSize(targetFile) != entry.size || force);
}
private IngestData discoverIngestData(string sourceDir, string targetDir, bool force) {
IngestData data;
foreach (DirEntry entry; dirEntries(sourceDir, SpanMode.shallow)) {
string targetFile = buildPath(targetDir, baseName(entry.name));
if (shouldCopyFile(entry, targetFile, force)) {
data.filesToCopy ~= entry;
}
}
return data;
}
/**
* Copies files from a source to a target directory.
* Params:
* sourceDir = The source directory.
* targetDir = The target directory.
* bufferSize = The buffer size to use when copying.
* force = Whether to overwrite existing files.
* dryRun = Whether to perform a dry-run.
* Returns: An exit code.
*/
public int copyFiles(string sourceDir, string targetDir, size_t bufferSize, bool force, bool dryRun) {
IngestData ingestData = discoverIngestData(sourceDir, targetDir, force);
if (ingestData.fileCount == 0) {
writeln("No new files to copy.");
return 0;
}
if (getAvailableDiskSpace(targetDir) < ingestData.totalFileSize) {
writefln!"Not enough disk space to copy all files: %s available, %s needed."(
formatFilesize(getAvailableDiskSpace(targetDir)),
formatFilesize(ingestData.totalFileSize)
);
return 1;
}
writefln!"Copying %d files (%s) to %s."(ingestData.fileCount, formatFilesize(ingestData.totalFileSize), targetDir);
if (dryRun) writeln("(Dry Run)");
if (!exists(targetDir) && !dryRun) mkdirRecurse(targetDir);
Bar progressBar = new FillingSquaresBar();
progressBar.width = 80;
progressBar.max = ingestData.totalFileSize;
progressBar.start();
ubyte[] buffer = new ubyte[bufferSize];
foreach (DirEntry entry; ingestData.filesToCopy) {
string filename = baseName(entry.name);
string targetFile = buildPath(targetDir, filename);
string verb = exists(targetFile) ? "Overwriting" : "Copying";
progressBar.message = { return std.string.format!"%s %s (%s)"(verb, filename, formatFilesize(entry.size)); };
if (!dryRun) {
File inputFile = File(entry.name, "rb");
File outputFile = File(targetFile, "wb");
foreach (ubyte[] localBuffer; inputFile.byChunk(buffer)) {
outputFile.rawWrite(localBuffer);
progressBar.next(localBuffer.length);
}
} else {
progressBar.next(getSize(entry.name));
}
}
progressBar.finish();
return 0;
}