From 52f15b4de14c3dbaa4969fb6f4e5382d47c230a5 Mon Sep 17 00:00:00 2001 From: Daniel Quinn Date: Sat, 5 Mar 2016 01:57:49 +0000 Subject: [PATCH] The first stages of getting thumbnails back --- .gitignore | 5 +- media/documents/originals/.keep | 0 media/documents/thumbnails/.keep | 0 .../migrations/0012_auto_20160305_0040.py | 101 ++++++++++++++++++ src/documents/models.py | 20 +++- src/documents/views.py | 8 +- src/paperless/urls.py | 6 +- 7 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 media/documents/originals/.keep create mode 100644 media/documents/thumbnails/.keep create mode 100644 src/documents/migrations/0012_auto_20160305_0040.py diff --git a/.gitignore b/.gitignore index d4c3fe38e..3c8b8ffea 100644 --- a/.gitignore +++ b/.gitignore @@ -57,7 +57,9 @@ docs/_build/ target/ # Stored PDFs -media/* +media/documents/*.gpg +media/documents/thumbnails/*.gpg +media/documents/originals/*.gpg # Sqlite database db.sqlite3 @@ -74,4 +76,3 @@ docker-compose.env # Used for development scripts/import-for-development environment - diff --git a/media/documents/originals/.keep b/media/documents/originals/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/media/documents/thumbnails/.keep b/media/documents/thumbnails/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/src/documents/migrations/0012_auto_20160305_0040.py b/src/documents/migrations/0012_auto_20160305_0040.py new file mode 100644 index 000000000..e42c6cde5 --- /dev/null +++ b/src/documents/migrations/0012_auto_20160305_0040.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.2 on 2016-03-05 00:40 +from __future__ import unicode_literals + +import gnupg +import os +import re +import shutil +import subprocess +import tempfile + +from django.conf import settings +from django.db import migrations + + +class GnuPG(object): + """ + A handy singleton to use when handling encrypted files. + """ + + gpg = gnupg.GPG(gnupghome=settings.GNUPG_HOME) + + @classmethod + def decrypted(cls, file_handle): + return cls.gpg.decrypt_file( + file_handle, passphrase=settings.PASSPHRASE).data + + @classmethod + def encrypted(cls, file_handle): + return cls.gpg.encrypt_file( + file_handle, + recipients=None, + passphrase=settings.PASSPHRASE, + symmetric=True + ).data + + +def move_documents_and_create_thumbnails(apps, schema_editor): + + documents = os.listdir(os.path.join(settings.MEDIA_ROOT, "documents")) + + if not documents: + return + + print("\n") + + for f in sorted(documents): + + if not f.endswith("gpg"): + continue + + print(" * Generating a thumbnail for {}".format(f)) + + thumb_temp = tempfile.mkdtemp( + prefix="paperless", dir=settings.SCRATCH_DIR) + orig_temp = tempfile.mkdtemp( + prefix="paperless", dir=settings.SCRATCH_DIR) + + orig_source = os.path.join(settings.MEDIA_ROOT, "documents", f) + orig_target = os.path.join(orig_temp, f.replace(".gpg", "")) + + with open(orig_source, "rb") as encrypted: + with open(orig_target, "wb") as unencrypted: + unencrypted.write(GnuPG.decrypted(encrypted)) + + subprocess.Popen(( + settings.CONVERT_BINARY, + "-scale", "500x500", + orig_target, + os.path.join(thumb_temp, "convert-%04d.jpg") + )).wait() + + thumb_source = os.path.join(thumb_temp, "convert-0000.jpg") + thumb_target = os.path.join( + settings.MEDIA_ROOT, + "documents", + "thumbnails", + re.sub(r"(\d+)\.\w+(\.gpg)", "\\1.jpg\\2", f) + ) + with open(thumb_source, "rb") as unencrypted: + with open(thumb_target, "wb") as encrypted: + encrypted.write(GnuPG.encrypted(unencrypted)) + + shutil.rmtree(thumb_temp) + shutil.rmtree(orig_temp) + + shutil.move( + os.path.join(settings.MEDIA_ROOT, "documents", f), + os.path.join(settings.MEDIA_ROOT, "documents", "originals", f), + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ('documents', '0011_auto_20160303_1929'), + ] + + operations = [ + migrations.RunPython(move_documents_and_create_thumbnails), + ] diff --git a/src/documents/models.py b/src/documents/models.py index a82f7643f..a3ffb8a74 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -171,6 +171,7 @@ def source_path(self): return os.path.join( settings.MEDIA_ROOT, "documents", + "originals", "{:07}.{}.gpg".format(self.pk, self.file_type) ) @@ -184,7 +185,24 @@ def file_name(self): @property def download_url(self): - return reverse("fetch", kwargs={"pk": self.pk}) + return reverse("fetch", kwargs={"kind": "doc", "pk": self.pk}) + + @property + def thumbnail_path(self): + return os.path.join( + settings.MEDIA_ROOT, + "documents", + "thumbnails", + "{:07}.jpg.gpg".format(self.pk) + ) + + @property + def thumbnail_file(self): + return open(self.thumbnail_path, "rb") + + @property + def thumbnail_url(self): + return reverse("fetch", kwargs={"kind": "thumb", "pk": self.pk}) class Log(models.Model): diff --git a/src/documents/views.py b/src/documents/views.py index ff7c4ce05..4a4a060bf 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -35,7 +35,7 @@ class FetchView(LoginRequiredMixin, DetailView): def render_to_response(self, context, **response_kwargs): """ - Override the default to return the unencrypted PDF as raw data. + Override the default to return the unencrypted image/PDF as raw data. """ content_types = { @@ -46,6 +46,12 @@ def render_to_response(self, context, **response_kwargs): Document.TYPE_TIF: "image/tiff", } + if self.kwargs["kind"] == "thumb": + return HttpResponse( + GnuPG.decrypted(self.object.thumb_file), + content_type=content_types[Document.TYPE_JPG] + ) + response = HttpResponse( GnuPG.decrypted(self.object.source_file), content_type=content_types[self.object.file_type] diff --git a/src/paperless/urls.py b/src/paperless/urls.py index 4b73dc88e..a7775a588 100644 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -44,7 +44,11 @@ # url(r"^$", IndexView.as_view(), name="index"), # File downloads - url(r"^fetch/(?P\d+)$", FetchView.as_view(), name="fetch"), + url( + r"^fetch/(?Pdoc|thumb)/(?P\d+)$", + FetchView.as_view(), + name="fetch" + ), # The Django admin url(r"admin/", admin.site.urls),