From 0493bf1b016cb47e215b2805127c4d68b00426d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20=C3=96hrstr=C3=B6m?= Date: Wed, 31 Jul 2024 22:58:17 +0200 Subject: [PATCH] Add gphoto2: storage. --- src/beak_cameramedia.cc | 125 ++++++++++++++++++++++++++++++++++++++++ src/configuration.cc | 18 ++++++ 2 files changed, 143 insertions(+) diff --git a/src/beak_cameramedia.cc b/src/beak_cameramedia.cc index 7beacb9..3044f48 100644 --- a/src/beak_cameramedia.cc +++ b/src/beak_cameramedia.cc @@ -28,6 +28,7 @@ #include "media.h" #include "storagetool.h" #include "storage_aftmtp.h" +#include "storage_gphoto2.h" #include "system.h" #include "util.h" @@ -185,6 +186,125 @@ RC import_aft_mtp_cli(Settings *settings, return rc; } +RC import_gphoto2(Settings *settings, + Monitor *monitor, + System *sys, + FileSystem *local_fs, + BeakImplementation *bi) +{ + assert(settings->from.storage->type == GPhoto2Storage); + //Path *home = Path::lookup(getenv("HOME")); + //Path *cache = home->append(".cache/beak/temp-beak-media-import"); + + local_fs->allowAccessTimeUpdates(); + + assert(settings->to.type == ArgDir); + Path *destination = settings->to.dir; + + // The directory name where we store the media files. + string archive_name = destination->name()->str(); + + // Establish access to the phone/camera and get the device name. + string device_name = gphoto2EstablishAccess(sys); + + info(CAMERA, "Importing media from %s into %s\n", device_name.c_str(), archive_name.c_str()); + + return RC::OK; +/* + unique_ptr progress = monitor->newProgressStatistics(buildJobName("listing", settings), "list"); + + map files; + + // Just list the files in the android phone, The crappy mtp protocol is soooo slow just listing the files. + // Anyway we do this once (hopefully unless there is a random disconnect) then we can check size and date + // for if we think we have imported this file already. I.e. no need to download slowly over mtp to do + // the full processing. + for (;;) + { + RC rc = aftmtpListFiles(settings->from.storage, + &files, + sys, + progress.get()); + + if (rc.isOk()) break; + // The mtp link crashed already in the first transfer.... Blech. + aftmtpReEstablishAccess(sys, true); + } + + UI::output("Found ... new files not yet in %s", archive_name.c_str()); + + vector> potential_files_to_copy; + for (auto &p : files) + { + bool already_exists = check_if_already_exists(p.first, p.second, local_fs, destination); + if (!already_exists) + { + potential_files_to_copy.push_back(p); + debug(CAMERA, "potential download %s\n", p.first->c_str()); + UI::clearLine(); + UI::output("Found %zu new files not yet in %s", potential_files_to_copy.size(), archive_name.c_str()); + } + } + UI::clearLine(); + if (potential_files_to_copy.size() == 0) + { + UI::clearLine(); + info(CAMERA, "All files imported into %s already.\n", archive_name.c_str()); + return RC::OK; + } + + // We have some potential files that we do not think have been imported yet. + // Lets download them into a temporary dir from which we can import them properly. + local_fs->mkDirpWriteable(cache); + + // Downloading from the phone/camera using mtp can take some time, lets track the progress. + progress = monitor->newProgressStatistics(buildJobName("copying", settings), "copy"); + + vector files_to_copy; + for (auto &p : potential_files_to_copy) + { + // Check if the file has already been copied to the temporary dir. + // Remember that the usb connection to the phone can break at any time. + // We want to pickup where we left off. + Path *dest_file = p.first->prepend(cache); + addWork(progress.get(), p.first, p.second, local_fs, dest_file, &files_to_copy); + // The file is added (or not) to files_to_copy. + } + + size_t already_in_cache = potential_files_to_copy.size()-files_to_copy.size(); + size_t already_imported = files.size()-files_to_copy.size(); + info(CAMERA, "Downloading %zu files (%zu already in cache and %zu already fully imported into %s).\n", + files_to_copy.size(), + already_in_cache, + already_imported, + archive_name.c_str()); + + progress->startDisplayOfProgress(); + RC rc = aftmtpFetchFiles(settings->from.storage, + &files_to_copy, + cache, + sys, + local_fs, + progress.get()); + + progress->finishProgress(); + + // Now import the cache into the real archive. + settings->from.type = ArgDir; + settings->from.dir = cache; + + settings->to.type = ArgStorage; + Storage st {}; + settings->to.storage = &st; + settings->to.storage->storage_location = destination; + settings->to.storage->type = FileSystemStorage; + + rc = bi->importMedia(settings, monitor); + + return rc; +*/ +} + RC BeakImplementation::cameraMedia(Settings *settings, Monitor *monitor) { assert(settings->from.type == ArgStorage); @@ -194,5 +314,10 @@ RC BeakImplementation::cameraMedia(Settings *settings, Monitor *monitor) return import_aft_mtp_cli(settings, monitor, sys_, local_fs_, this); } + if (settings->from.storage->type == GPhoto2Storage) + { + return import_gphoto2(settings, monitor, sys_, local_fs_, this); + } + return RC::ERR; } diff --git a/src/configuration.cc b/src/configuration.cc index 668660f..43e856f 100644 --- a/src/configuration.cc +++ b/src/configuration.cc @@ -139,6 +139,7 @@ class ConfigurationImplementation : public Configuration bool isRCloneStorage(Path *storage_location, string *type = NULL); bool isRSyncStorage(Path *storage_location); bool isAftMtpStorage(Path *storage_location); + bool isGPhoto2Storage(Path *storage_location); // Map rule name to rule. map rules_; @@ -1133,6 +1134,17 @@ bool ConfigurationImplementation::isAftMtpStorage(Path *storage_location) return prefix == "aftmtp:"; } +bool ConfigurationImplementation::isGPhoto2Storage(Path *storage_location) +{ + string arg = storage_location->str(); + auto colon = arg.find(':'); + if (colon == string::npos) return false; + + string prefix = arg.substr(0,colon+1); + + return prefix == "gphoto2:"; +} + // Storage created on the fly depending on the command line arguments. Storage a_storage; @@ -1166,6 +1178,12 @@ Storage *ConfigurationImplementation::findStorageFrom(Path *storage_location, Co a_storage.storage_location = storage_location; return &a_storage; } + else if (isGPhoto2Storage(storage_location)) + { + a_storage.type = GPhoto2Storage; + a_storage.storage_location = storage_location; + return &a_storage; + } else { Path *rp = storage_location->realpath();