diff --git a/pount/apps/api/signals.py b/pount/apps/api/signals.py index 686dbad5d96e5186848bfdf87212cc6012e6cead..3b1d094848095c84dfa495c0741408934628ec41 100644 --- a/pount/apps/api/signals.py +++ b/pount/apps/api/signals.py @@ -5,7 +5,6 @@ from django.dispatch import receiver from pount.apps.api import models from pount.apps.api.libs.referential import clear_cache_for_referential, delete_referential_in_mongodb from pount.apps.api.models.template import DELETED_SUFFIX -from pount.apps.api.tasks import clean_iiif_media, ensure_media_has_iiif pre_soft_delete = django.dispatch.Signal() @@ -52,15 +51,3 @@ def _create_project_group_and_permission(project, group_name, role_name): content_object=project, role=models.permission.Role.objects.get(name=role_name), ) - - -@receiver(post_save, sender=models.item.MediaFile) -def create_iiif_task(sender, instance: models.item.MediaFile, **kwargs): - if instance.component == "Image": - ensure_media_has_iiif.delay_on_commit(instance.id) - - -@receiver(post_delete, sender=models.item.MediaFile) -def remove_iiif_file(sender, instance: models.item.MediaFile, **kwargs): - if instance.component == "Image": - clean_iiif_media.delay_on_commit(str(instance.id)) diff --git a/pount/apps/api/tasks.py b/pount/apps/api/tasks.py index 4dedc1cabc870168d550e3790ff401d45444fef4..8359b82fee816142a9ef6243f9af03939cbc9285 100644 --- a/pount/apps/api/tasks.py +++ b/pount/apps/api/tasks.py @@ -1,5 +1,4 @@ import logging -from typing import Union from uuid import UUID from celery import shared_task @@ -32,76 +31,3 @@ def handle_update_item_metadata_on_template_save(template_id: UUID) -> None: from pount.apps.api.libs.template import update_items_using_template update_items_using_template(template=template_id) - - -@shared_task -def clean_iiif_media(media_id: Union[str, UUID]) -> None: - from pount.apps.api.storage_backends import IIIFFileStorage - - iiif_storage = IIIFFileStorage() - tiled_tif = f"{media_id}.tiled.tif" - iiif_storage.delete(tiled_tif) - logger.debug(f"deleted {tiled_tif}") - - -@shared_task -def ensure_media_has_iiif(media_id: Union[str, UUID]) -> None: - import os - import subprocess - import tempfile - from pathlib import Path - from typing import Union - - import requests - - from pount.apps.api.libs.file import item_mediafile_fullname - from pount.apps.api.models.item import MediaFile - from pount.apps.api.storage_backends import IIIFFileStorage, ItemMediaFileStorage - - def pic_to_tiled_tiff(source: Union[str, Path], destination: Union[str, Path], dpi: int = 300): - tif_source = f"{source}.delme.tif" - _, extension = os.path.splitext(source) - if extension.lower() in [".pdf", ".ai"]: - pdftoppm_dest = f"{source}.delme" # pdftoppm adds .tif extension already - subprocess.run(["pdftoppm", "-singlefile", "-tiff", source, "-r", str(dpi), pdftoppm_dest]) - else: - subprocess.run(["convert", "-quiet", "-auto-orient", source, tif_source]) - subprocess.run( - [ - "vips", - "im_vips2tiff", - tif_source, - f"{destination}:deflate,tile:256x256,pyramid", - ] - ) - os.remove(tif_source) - - media_file: MediaFile = MediaFile.objects.filter(id=media_id).first() - - if not media_file: - logger.debug(f"no such media file: {media_id}") - return - - item_media_storage = ItemMediaFileStorage() - full_name = item_mediafile_fullname(media_file.filename, media_file.item) - - if not item_media_storage.exists(full_name): - logger.debug(f"media file not on storage: {media_id} / {full_name}") - return - - with tempfile.TemporaryDirectory() as tmp_dirname: - logger.debug(f"Start tiling in {tmp_dirname}") - response = requests.get(media_file.url, stream=True) - if response.ok: - tmp_file_name = Path(tmp_dirname, media_file.filename) - basename, _ = os.path.splitext(tmp_file_name) - with open(tmp_file_name, "wb") as f: - for chunk in response.iter_content(chunk_size=1024): - if chunk: - f.write(chunk) - tiled_tif = f"{basename}.tiled.tif" - pic_to_tiled_tiff(tmp_file_name, tiled_tif) - iiif_storage = IIIFFileStorage() - with open(tiled_tif, "rb") as f: - iiif_storage.save(f"{media_file.id}.tiled.tif", f) - logger.debug(f"saved {tiled_tif}") diff --git a/pount/apps/iiif/apps.py b/pount/apps/iiif/apps.py index 08c5b2d62d20ff0e24139619bbb08e0a4fbf1607..cf8b217ec3a560b10611479999733d1a22e5d02f 100644 --- a/pount/apps/iiif/apps.py +++ b/pount/apps/iiif/apps.py @@ -3,3 +3,6 @@ from django.apps import AppConfig class IiifAppConfig(AppConfig): name = "pount.apps.iiif" + + def ready(self): + import pount.apps.iiif.signals # noqa:F401 diff --git a/pount/apps/iiif/signals.py b/pount/apps/iiif/signals.py new file mode 100644 index 0000000000000000000000000000000000000000..efeed65192ae2e446ffc1142aa51cf40c37bfad5 --- /dev/null +++ b/pount/apps/iiif/signals.py @@ -0,0 +1,17 @@ +from django.db.models.signals import post_delete, post_save +from django.dispatch import receiver + +from pount.apps.api import models +from pount.apps.iiif.tasks import clean_iiif_media, ensure_media_has_iiif + + +@receiver(post_save, sender=models.item.MediaFile) +def create_iiif_task(sender, instance: models.item.MediaFile, **kwargs): + if instance.component == "Image": + ensure_media_has_iiif.delay_on_commit(instance.id) + + +@receiver(post_delete, sender=models.item.MediaFile) +def remove_iiif_file(sender, instance: models.item.MediaFile, **kwargs): + if instance.component == "Image": + clean_iiif_media.delay_on_commit(str(instance.id)) diff --git a/pount/apps/iiif/tasks.py b/pount/apps/iiif/tasks.py new file mode 100644 index 0000000000000000000000000000000000000000..eddc119719217cf3991bcce7fa6e84775adf548d --- /dev/null +++ b/pount/apps/iiif/tasks.py @@ -0,0 +1,80 @@ +import logging +from typing import Union +from uuid import UUID + +from celery import shared_task + +logger = logging.getLogger(__name__) + + +@shared_task +def clean_iiif_media(media_id: Union[str, UUID]) -> None: + from pount.apps.api.storage_backends import IIIFFileStorage + + iiif_storage = IIIFFileStorage() + tiled_tif = f"{media_id}.tiled.tif" + iiif_storage.delete(tiled_tif) + logger.debug(f"deleted {tiled_tif}") + + +@shared_task +def ensure_media_has_iiif(media_id: Union[str, UUID]) -> None: + import os + import subprocess + import tempfile + from pathlib import Path + from typing import Union + + import requests + + from pount.apps.api.libs.file import item_mediafile_fullname + from pount.apps.api.models.item import MediaFile + from pount.apps.api.storage_backends import IIIFFileStorage, ItemMediaFileStorage + + def pic_to_tiled_tiff(source: Union[str, Path], destination: Union[str, Path], dpi: int = 300): + tif_source = f"{source}.delme.tif" + _, extension = os.path.splitext(source) + if extension.lower() in [".pdf", ".ai"]: + pdftoppm_dest = f"{source}.delme" # pdftoppm adds .tif extension already + subprocess.run(["pdftoppm", "-singlefile", "-tiff", source, "-r", str(dpi), pdftoppm_dest]) + else: + subprocess.run(["convert", "-quiet", "-auto-orient", source, tif_source]) + subprocess.run( + [ + "vips", + "im_vips2tiff", + tif_source, + f"{destination}:deflate,tile:256x256,pyramid", + ] + ) + os.remove(tif_source) + + media_file: MediaFile = MediaFile.objects.filter(id=media_id).first() + + if not media_file: + logger.debug(f"no such media file: {media_id}") + return + + item_media_storage = ItemMediaFileStorage() + full_name = item_mediafile_fullname(media_file.filename, media_file.item) + + if not item_media_storage.exists(full_name): + logger.debug(f"media file not on storage: {media_id} / {full_name}") + return + + with tempfile.TemporaryDirectory() as tmp_dirname: + logger.debug(f"Start tiling in {tmp_dirname}") + response = requests.get(media_file.url, stream=True) + if response.ok: + tmp_file_name = Path(tmp_dirname, media_file.filename) + basename, _ = os.path.splitext(tmp_file_name) + with open(tmp_file_name, "wb") as f: + for chunk in response.iter_content(chunk_size=1024): + if chunk: + f.write(chunk) + tiled_tif = f"{basename}.tiled.tif" + pic_to_tiled_tiff(tmp_file_name, tiled_tif) + iiif_storage = IIIFFileStorage() + with open(tiled_tif, "rb") as f: + iiif_storage.save(f"{media_file.id}.tiled.tif", f) + logger.debug(f"saved {tiled_tif}") diff --git a/pount/settings/common.py b/pount/settings/common.py index dc19a24404a38e33314ce5db566fb283affd3111..b707df131e8156f2d609bbc989f00feba9333565 100644 --- a/pount/settings/common.py +++ b/pount/settings/common.py @@ -336,6 +336,7 @@ LOCAL_APPS = [ "pount.apps.api", "pount.apps.public", "pount.apps.search", + "pount.apps.iiif", ] INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS