Further split up stuff.
This commit is contained in:
parent
5b24b3380b
commit
3a1587be5e
11
README.md
11
README.md
|
@ -22,4 +22,15 @@ This tool comes with a variety of options, which you can view by running `gopro-
|
|||
- `--force` - Forcibly overwrite existing files.
|
||||
- `--dryRun` - Perform a dry-run (and don't actually copy anything).
|
||||
- `--bufferSize` - The size of the memory buffer for copying.
|
||||
- `--clean` - Delete copied items from the GoPro's media card afterwards.
|
||||
- `--help` - Shows help information.
|
||||
|
||||
### System Compatibility
|
||||
Currently, this software is known to work with the following GoPro models:
|
||||
- Hero 9
|
||||
|
||||
And the following operating systems:
|
||||
- Linux/Ubuntu
|
||||
- MacOS
|
||||
|
||||
If you've got the time and hardware, I'd greatly appreciate if you could test this software on your own device, and make a PR to add it to the list here, or an issue if something doesn't work.
|
||||
|
|
36
source/app.d
36
source/app.d
|
@ -11,9 +11,8 @@ import progress;
|
|||
import utils;
|
||||
import ingest;
|
||||
|
||||
const DEFAULT_OUTPUT_DIR = "raw";
|
||||
const DEFAULT_MEDIA_DIR = "/media";
|
||||
const DEFAULT_BUFFER_SIZE = 1024 * 1024;
|
||||
const DEFAULT_OUTPUT_DIR = "raw";
|
||||
|
||||
int main(string[] args) {
|
||||
writeln(
|
||||
|
@ -25,30 +24,29 @@ int main(string[] args) {
|
|||
"| |\n" ~
|
||||
"+---------------------------------+\n"
|
||||
);
|
||||
|
||||
IngestConfig config;
|
||||
config.outputDir = buildPath(getcwd(), DEFAULT_OUTPUT_DIR);
|
||||
string mediaSearchDir = DEFAULT_MEDIA_DIR;
|
||||
string outputDir = buildPath(getcwd(), DEFAULT_OUTPUT_DIR);
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE;
|
||||
bool force = false;
|
||||
bool dryRun = false;
|
||||
|
||||
auto helpInfo = getopt(
|
||||
args,
|
||||
"mediaDir|i",
|
||||
format!"The base directory from which to search for the GoPro media. Defaults to \"%s\"."(mediaSearchDir),
|
||||
&mediaSearchDir,
|
||||
"outputDir|o",
|
||||
format!"The directory to copy data to. Defaults to \"%s\". Will create the directory if it doesn't exist yet."(outputDir),
|
||||
&outputDir,
|
||||
format!"The directory to copy data to. Defaults to \"%s\". Will create the directory if it doesn't exist yet."(config.outputDir),
|
||||
&config.outputDir,
|
||||
"force|f",
|
||||
format!"Whether to forcibly overwrite existing files. Defaults to %s."(force),
|
||||
&force,
|
||||
format!"Whether to forcibly overwrite existing files. Defaults to %s."(config.force),
|
||||
&config.force,
|
||||
"dryRun|d",
|
||||
format!"Whether to perform a dry-run (don't actually copy anything). Defaults to %s."(dryRun),
|
||||
&dryRun,
|
||||
format!"Whether to perform a dry-run (don't actually copy anything). Defaults to %s."(config.dryRun),
|
||||
&config.dryRun,
|
||||
"bufferSize|b",
|
||||
format!"The size of the buffer for copying files, in bytes. Defaults to %s."(formatFilesize(DEFAULT_BUFFER_SIZE)),
|
||||
&bufferSize
|
||||
format!"The size of the buffer for copying files, in bytes. Defaults to %s."(formatFilesize(config.bufferSize)),
|
||||
&config.bufferSize,
|
||||
"clean|c",
|
||||
format!"Whether to remove files from the GoPro media card after copying. Defaults to %s."(config.clean),
|
||||
&config.clean
|
||||
);
|
||||
|
||||
if (helpInfo.helpWanted) {
|
||||
|
@ -61,7 +59,7 @@ int main(string[] args) {
|
|||
writeln("Couldn't find GoPro directory.");
|
||||
return 1;
|
||||
}
|
||||
string goProDir = nullableGoProDir.get();
|
||||
writefln!"Found GoPro media at %s."(goProDir);
|
||||
return copyFiles(goProDir, outputDir, bufferSize, force, dryRun);
|
||||
config.inputDir = nullableGoProDir.get();
|
||||
writefln!"Found GoPro media at %s."(config.inputDir);
|
||||
return copyFiles(config);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import std.typecons;
|
|||
import filesizes;
|
||||
import progress;
|
||||
|
||||
const DEFAULT_BUFFER_SIZE = 1024 * 1024;
|
||||
|
||||
private struct IngestData {
|
||||
DirEntry[] filesToCopy;
|
||||
|
||||
|
@ -21,6 +23,15 @@ private struct IngestData {
|
|||
}
|
||||
}
|
||||
|
||||
public struct IngestConfig {
|
||||
string inputDir;
|
||||
string outputDir;
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE;
|
||||
bool force = false;
|
||||
bool dryRun = false;
|
||||
bool clean = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we should copy a given file from the media card to the local
|
||||
* directory.
|
||||
|
@ -36,11 +47,17 @@ private bool shouldCopyFile(DirEntry entry, string targetFile, bool force) {
|
|||
(!exists(targetFile) || getSize(targetFile) != entry.size || force);
|
||||
}
|
||||
|
||||
private IngestData discoverIngestData(string sourceDir, string targetDir, bool force) {
|
||||
/**
|
||||
* Searches for relevant files to ingest.
|
||||
* Params:
|
||||
* config = The ingest config.
|
||||
* Returns: Data about what to ingest.
|
||||
*/
|
||||
private IngestData discoverIngestData(IngestConfig config) {
|
||||
IngestData data;
|
||||
foreach (DirEntry entry; dirEntries(sourceDir, SpanMode.shallow)) {
|
||||
string targetFile = buildPath(targetDir, baseName(entry.name));
|
||||
if (shouldCopyFile(entry, targetFile, force)) {
|
||||
foreach (DirEntry entry; dirEntries(config.inputDir, SpanMode.shallow)) {
|
||||
string targetFile = buildPath(config.outputDir, baseName(entry.name));
|
||||
if (shouldCopyFile(entry, targetFile, config.force)) {
|
||||
data.filesToCopy ~= entry;
|
||||
}
|
||||
}
|
||||
|
@ -50,42 +67,38 @@ private IngestData discoverIngestData(string sourceDir, string targetDir, bool f
|
|||
/**
|
||||
* 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.
|
||||
* config = The configuration for the ingest operation.
|
||||
* Returns: An exit code.
|
||||
*/
|
||||
public int copyFiles(string sourceDir, string targetDir, size_t bufferSize, bool force, bool dryRun) {
|
||||
IngestData ingestData = discoverIngestData(sourceDir, targetDir, force);
|
||||
public int copyFiles(IngestConfig config) {
|
||||
IngestData ingestData = discoverIngestData(config);
|
||||
if (ingestData.fileCount == 0) {
|
||||
writeln("No new files to copy.");
|
||||
return 0;
|
||||
}
|
||||
if (getAvailableDiskSpace(targetDir) < ingestData.totalFileSize) {
|
||||
if (getAvailableDiskSpace(config.outputDir) < ingestData.totalFileSize) {
|
||||
writefln!"Not enough disk space to copy all files: %s available, %s needed."(
|
||||
formatFilesize(getAvailableDiskSpace(targetDir)),
|
||||
formatFilesize(getAvailableDiskSpace(config.outputDir)),
|
||||
formatFilesize(ingestData.totalFileSize)
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
writefln!"Copying %d files (%s) to %s."(ingestData.fileCount, formatFilesize(ingestData.totalFileSize), targetDir);
|
||||
if (dryRun) writeln("(Dry Run)");
|
||||
writefln!"Copying %d files (%s) to %s."(ingestData.fileCount, formatFilesize(ingestData.totalFileSize), config.outputDir);
|
||||
if (config.dryRun) writeln("(Dry Run)");
|
||||
|
||||
if (!exists(targetDir) && !dryRun) mkdirRecurse(targetDir);
|
||||
if (!exists(config.outputDir) && !config.dryRun) mkdirRecurse(config.outputDir);
|
||||
|
||||
Bar progressBar = new FillingSquaresBar();
|
||||
progressBar.width = 80;
|
||||
progressBar.max = ingestData.totalFileSize;
|
||||
progressBar.start();
|
||||
ubyte[] buffer = new ubyte[bufferSize];
|
||||
ubyte[] buffer = new ubyte[config.bufferSize];
|
||||
foreach (DirEntry entry; ingestData.filesToCopy) {
|
||||
string filename = baseName(entry.name);
|
||||
string targetFile = buildPath(targetDir, filename);
|
||||
string targetFile = buildPath(config.outputDir, filename);
|
||||
string verb = exists(targetFile) ? "Overwriting" : "Copying";
|
||||
progressBar.message = { return std.string.format!"%s %s (%s)"(verb, filename, formatFilesize(entry.size)); };
|
||||
if (!dryRun) {
|
||||
if (!config.dryRun) {
|
||||
File inputFile = File(entry.name, "rb");
|
||||
File outputFile = File(targetFile, "wb");
|
||||
foreach (ubyte[] localBuffer; inputFile.byChunk(buffer)) {
|
||||
|
|
Loading…
Reference in New Issue