aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2016-09-04 22:31:45 -0600
committerJason A. Donenfeld <Jason@zx2c4.com>2016-09-07 22:08:21 -0600
commit71b613fa066a7d1825265347a99a4b9fd263cac5 (patch)
tree7cff296dad8001b6585b84e5e57cafa24389451e
downloadclockphoto-71b613fa066a7d1825265347a99a4b9fd263cac5.tar.xz
clockphoto-71b613fa066a7d1825265347a99a4b9fd263cac5.zip
Initial commit.1.0
-rw-r--r--README.md18
-rw-r--r--clockphoto.pro6
-rw-r--r--main.cpp35
-rw-r--r--widget.cpp72
-rw-r--r--widget.h14
5 files changed, 145 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0da8fb1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# Clockphoto
+##### by [Jason A. Donenfeld](mailto:jason@zx2c4.com) of [zx2c4.com](https://www.zx2c4.com/)
+
+Clockphoto adjusts a directory of photographs based on an image of a clock.
+
+ $ clockphoto ./clock-photo.jpg /path/to/images
+
+This will open a window asking you to enter the correct date and time of the clock photo. It then calculates the difference between what you entered and the EXIF timestamp of the photo, and adjusts all photos in the directory of photos by that difference.
+
+If the image directory is unspecified, it is implicitly the enclosing directory of the clock photo.
+
+### Building
+
+This requires `exiv2` and `qt5`.
+
+ $ qmake
+ $ make
+ $ ./clockphoto CLOCK_PHOTO [IMAGE_DIRECTORY]
diff --git a/clockphoto.pro b/clockphoto.pro
new file mode 100644
index 0000000..fd7ea04
--- /dev/null
+++ b/clockphoto.pro
@@ -0,0 +1,6 @@
+QT += core gui widgets
+CONFIG += c++11
+TARGET = clockphoto
+TEMPLATE = app
+SOURCES += main.cpp widget.cpp
+HEADERS += widget.h
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..f40d93b
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,35 @@
+#include "widget.h"
+#include <QApplication>
+#include <QFile>
+#include <QDir>
+
+static void usage(const char *progname)
+{
+ fprintf(stderr, "Usage: %s CLOCK_PHOTO [PHOTO_DIRECTORY]\n\nThis program adds or subtracts a time offset from the EXIF metadata of\nphotos in PHOTO_DIRECTORY based on a photo of a clock in CLOCK_PHOTO. If\nPHOTO_DIRECTORY is unspecified, it is assumed to be the enclosing\ndirectory of CLOCK_PHOTO. It then sets the file mtime to the timestamp.\n\nCopyright (C) Jason A. Donenfeld <jason@zx2c4.com>. All Rights Reserved.\nSee www.zx2c4.com for more information.\n", progname);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ QDir dir;
+ if (argc != 2 && argc != 3)
+ usage(argv[0]);
+ if (!QFile::exists(argv[1]))
+ usage(argv[0]);
+ if (argc == 3)
+ dir = QDir(argv[2]);
+ else
+ dir = QFileInfo(argv[1]).dir();
+ if (!dir.exists())
+ usage(argv[0]);
+ const QStringList photos = dir.entryList(QStringList() << "*.jpg", QDir::Files | QDir::Readable | QDir::Writable, QDir::Name);
+ const QString clockPhoto = argv[1];
+ if (photos.isEmpty())
+ usage(argv[0]);
+
+ QApplication a(argc, argv);
+ Widget w(photos, dir.absolutePath(), clockPhoto);
+ w.show();
+ w.setFixedSize(w.size());
+ return a.exec();
+}
diff --git a/widget.cpp b/widget.cpp
new file mode 100644
index 0000000..c7b6375
--- /dev/null
+++ b/widget.cpp
@@ -0,0 +1,72 @@
+#include "widget.h"
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QDateTimeEdit>
+#include <QProcess>
+#include <QPushButton>
+#include <QApplication>
+#include <QDialogButtonBox>
+
+Widget::Widget(const QStringList &photos, const QString &dir, const QString &clockPhoto, QWidget *parent) : QDialog(parent)
+{
+ QProcess p;
+ p.start("exiv2", QStringList() << clockPhoto << "-K" << "Exif.Image.DateTime" << "-Pv", QProcess::ReadOnly);
+ p.waitForFinished(-1);
+ const QDateTime clockDate = QDateTime::fromString(p.readAllStandardOutput(), "yyyy:MM:dd HH:mm:ss\n");
+ setWindowTitle(tr("Clock Photo Time Adjuster"));
+ QVBoxLayout *layout = new QVBoxLayout;
+ if (photos.count() == 1)
+ layout->addWidget(new QLabel(tr("Adjusting dates for: <pre>%1</pre>").arg(photos.at(0))));
+ else
+ layout->addWidget(new QLabel(tr("Adjusting dates for %1 photos in: <pre>%2</pre>").arg(photos.count()).arg(dir)));
+ QLabel *image = new QLabel;
+ image->setPixmap(QPixmap(clockPhoto).scaledToWidth(800, Qt::SmoothTransformation));
+ layout->addWidget(image);
+ QDateTimeEdit *date = new QDateTimeEdit;
+ date->setDisplayFormat("MMMM d, yyyy HH:mm:ss");
+ date->setDateTime(clockDate);
+ layout->addWidget(date);
+ const QString defaultText = tr("Choose the date and time of the clock photo above.");
+ QLabel *adjustment = new QLabel(defaultText);
+ layout->addWidget(adjustment);
+ QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Cancel);
+ buttons->button(QDialogButtonBox::Apply)->setText(tr("&Adjust dates"));
+ buttons->button(QDialogButtonBox::Apply)->setEnabled(false);
+ layout->addWidget(buttons);
+ setLayout(layout);
+
+ connect(date, &QDateTimeEdit::dateTimeChanged, this, [=]() {
+ qint64 diff = date->dateTime().secsTo(clockDate);
+ qint64 days = qAbs(diff) / 86400;
+ QTime difference = QTime(0, 0).addSecs(qAbs(diff) % 86400);
+ if (diff != 0) {
+ adjustment->setText(QString("%1 %2 days, %3 hours, %4 minutes, %5 seconds").arg(diff < 0 ? tr("Add") : tr("Subtract")).arg(days).arg(difference.hour()).arg(difference.minute()).arg(difference.second()));
+ buttons->button(QDialogButtonBox::Apply)->setEnabled(true);
+ } else {
+ adjustment->setText(defaultText);
+ buttons->button(QDialogButtonBox::Apply)->setEnabled(false);
+ }
+ });
+
+ connect(buttons, &QDialogButtonBox::accepted, this, [=]() {
+ qint64 diff = clockDate.secsTo(date->dateTime());
+ QString what;
+ if (diff < 0)
+ what += "-";
+ diff = qAbs(diff);
+ qint64 hours = diff / (60 * 60);
+ diff = diff % (60 * 60);
+ qint64 minutes = diff / 60;
+ qint64 seconds = diff % 60;
+ what += QString("%1:%2:%3").arg(hours, 2, 10, QChar('0')).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
+ QProcess p;
+ p.setWorkingDirectory(dir);
+ p.start("exiv2", QStringList() << "-a" << what << photos);
+ p.waitForFinished(-1);
+ p.start("exiv2", QStringList() << "-T" << photos);
+ p.waitForFinished(-1);
+ close();
+ });
+
+ connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::close);
+}
diff --git a/widget.h b/widget.h
new file mode 100644
index 0000000..fc92cf9
--- /dev/null
+++ b/widget.h
@@ -0,0 +1,14 @@
+#ifndef WIDGET_H
+#define WIDGET_H
+
+#include <QDialog>
+
+class Widget : public QDialog
+{
+ Q_OBJECT
+
+public:
+ Widget(const QStringList &photos, const QString &dir, const QString &clockPhoto, QWidget *parent = 0);
+};
+
+#endif // WIDGET_H