Skip to content

Commit

Permalink
Add new 'checksums' command, to add sha1 or md5 machine tags.
Browse files Browse the repository at this point in the history
Refs #20
  • Loading branch information
samwilson committed Feb 4, 2018
1 parent a5c5749 commit 9d6a04e
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 4 deletions.
2 changes: 2 additions & 0 deletions bin/flickr-cli
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ if (isset($autoloader)) {

use Symfony\Component\Console\Application;

use TheFox\FlickrCli\Command\ChecksumsCommand;
use TheFox\FlickrCli\Command\PiwigoCommand;
use TheFox\FlickrCli\FlickrCli;
use TheFox\FlickrCli\Command\AlbumsCommand;
Expand All @@ -32,6 +33,7 @@ use TheFox\FlickrCli\Command\UploadCommand;
$application = new Application(FlickrCli::NAME, FlickrCli::VERSION);
$application->add(new AlbumsCommand());
$application->add(new AuthCommand());
$application->add(new ChecksumsCommand());
$application->add(new DeleteCommand());
$application->add(new DownloadCommand());
$application->add(new FilesCommand());
Expand Down
2 changes: 1 addition & 1 deletion src/TheFox/FlickrCli/Command/AuthCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ private function getPermissionType(): string
$question = 'Please select from the following three options';
$choices = [
'read' => 'download photos',
'write' => 'upload photos',
'write' => 'upload or edit photos or their metadata',
'delete' => 'download and/or delete photos from Flickr',
];

Expand Down
164 changes: 164 additions & 0 deletions src/TheFox/FlickrCli/Command/ChecksumsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php

namespace TheFox\FlickrCli\Command;

use Exception;
use Rezzza\Flickr\ApiFactory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;

class ChecksumsCommand extends DownloadCommand
{

/** @var string */
protected $tmpDir;

/** @var ApiFactory */
protected $apiFactory;

protected function configure()
{
FlickrCliCommand::configure();
$this->setName('checksums');
$this->setDescription('Add checksum machine tags to photos already on Flickr.');
$this->addOption('hash', null, InputOption::VALUE_OPTIONAL, 'The hash function to use.', 'sha1');
$this->addOption('duplicates', 'd', InputOption::VALUE_NONE, "Search for duplicates,\nand output search-result URLs when found.");
}

/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
FlickrCliCommand::execute($input, $output);
$this->apiFactory = $this->getApiService()->getApiFactory();

// Set up the temporary directory.
$this->tmpDir = sys_get_temp_dir() . '/flickr-cli';
$filesystem = new Filesystem();
if (!$filesystem->exists($this->tmpDir)) {
$filesystem->mkdir($this->tmpDir, 0755);
}

// Get all photos.
$page = 1;
do {
pcntl_signal_dispatch();
if ($this->getExit()) {
break;
}
$params = [
'user_id' => 'me',
'page' => $page,
'per_page' => 500,
'extras' => 'o_url, tags',
];
$photos = $this->apiFactory->call('flickr.people.getPhotos', $params);
$this->getOutput()->writeln("Page $page of " . $photos->photos['pages']);
foreach ($photos->photos->photo as $photo) {
// Break if required.
pcntl_signal_dispatch();
if ($this->getExit()) {
break;
}

// Process this photo.
$hashTag = $this->processPhoto($photo);
if (!$hashTag) {
exit();
continue;
}

// If desired, perform a search for this tag.
$dupes = $this->getInput()->getOption('duplicates');
if ($dupes) {
$search = $this->apiFactory->call('flickr.photos.search', ['machine_tags' => $hashTag]);
if ((int)$search->photos['total'] > 1) {
$url = "https://www.flickr.com/photos/tags/$hashTag";
$this->getLogger()->alert("Duplicate found: $url");
}
}
}
$page++;
} while ($photos->photos['page'] !== $photos->photos['pages']);

// Clean up the temporary directory.
foreach (preg_grep('|^\..*|', scandir($this->tmpDir), PREG_GREP_INVERT) as $file) {
unlink($file);
}
rmdir($this->tmpDir);

return $this->getExit();
}

/**
* @param $photo
* @return string|bool The hash machine tag, or false.
* @throws Exception
*/
protected function processPhoto($photo)
{
// Find the hash function.
$hash = $this->getInput()->getOption('hash');
$hashFunction = $hash . '_file';
if (!function_exists($hashFunction)) {
throw new Exception("Hash function not available: $hashFunction");
}

// See if the photo has already got a checksum tag.
preg_match("/(checksum:$hash=.*)/", $photo['tags'], $matches);
if (isset($matches[1])) {
// If it's already got a tag, do nothing more.
$this->getLogger()->info(sprintf('Already has checksum: %s', $photo['id']));
return $matches[1];
}

$this->getLogger()->info(sprintf('Adding checksum machine tag to: %s', $photo['id']));

// Download the file.
$tmpFilename = 'checksumming';
$downloaded = $this->downloadPhoto($photo, $this->tmpDir, $tmpFilename);
if (false === $downloaded) {
$this->getLogger()->error(sprintf('Unable to download: %s', $photo['id']));
return false;
}

// Calculate the file's hash, and remove the temporary file.
$filename = $this->tmpDir . '/' . $tmpFilename . '.' . $downloaded['originalformat'];
$fileHash = $hashFunction($filename);
if (file_exists($filename)) {
unlink($filename);
}

// Upload the new tag if it's not already present.
$hashTag = "checksum:$hash=$fileHash";
$tagAdded = $this->apiFactory->call('flickr.photos.setTags', [
'photo_id' => $photo['id'],
'tags' => $this->getTagsAsString($photo['id']) . ' ' . $hashTag,
]);
if (isset($tagAdded->err)) {
throw new Exception($tagAdded->err['msg']);
}
return $hashTag;
}

/**
* Get a space-separated string of all tags.
* @param int $photoId The photo ID.
* @return string
*/
protected function getTagsAsString($photoId)
{
$photoInfo = $this->apiFactory->call('flickr.photos.getInfo', ['photo_id' => $photoId]);
$tags = [];
foreach ($photoInfo->photo->tags->tag as $tag) {
$tags[] = '"'.$tag['raw'].'"';
}
$tagsString = join(' ', $tags);
return $tagsString;
}
}
7 changes: 4 additions & 3 deletions src/TheFox/FlickrCli/Command/DownloadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
use Rych\ByteSize\ByteSize;
use TheFox\FlickrCli\FlickrCli;

final class DownloadCommand extends FlickrCliCommand
class DownloadCommand extends FlickrCliCommand
{
/**
* @var string The destination directory for downloaded files. No trailing slash.
Expand Down Expand Up @@ -246,12 +246,13 @@ private function downloadByAlbumTitle(): int
* additional 'filesize' property will be set on the return element.
*
* @param SimpleXMLElement $photo
* @param string $destinationPath
* @param string $destinationPath The full filesystem path to the directory into which to download the photo. Must
* already exist.
* @param string $basename The filename to save the downloaded file to (without extension).
* @return SimpleXMLElement|boolean Photo metadata as returned by Flickr, or false if something went wrong.
* @throws Exception
*/
private function downloadPhoto(SimpleXMLElement $photo, string $destinationPath, string $basename = null)
protected function downloadPhoto(SimpleXMLElement $photo, string $destinationPath, string $basename = null)
{
$id = (string)$photo->attributes()->id;

Expand Down

0 comments on commit 9d6a04e

Please sign in to comment.