jpeg_reader.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. """
  2. This plugin uses the metadata from JPEG images (EXIF and IPTC) to construct a meaningful page or gallery.
  3. Possible uses are gallery pages or a blog article that's mainly about an image.
  4. With this tool, it's posible to just dump an image without any extra data/linkage to create coherent output.
  5. The note here is that the extension is `jpeg_article` so it doesn't pick up {attach} or other static resources.
  6. """
  7. import logging
  8. from datetime import datetime
  9. from os import makedirs, sep
  10. from os.path import join, dirname, isdir, splitext
  11. from typing import Tuple
  12. from PIL import Image
  13. from pelican import signals
  14. from pelican.readers import BaseReader
  15. from pelican.urlwrappers import URLWrapper, Category, Author, Tag
  16. from .constants import Exiv, PelicanConfig, PelicanMetadata, PelicanClass
  17. from .exiv2_parser import Exiv2Parser
  18. class JpegReader(BaseReader):
  19. logger = logging.getLogger('JpegReader')
  20. enabled = True
  21. file_extensions = ('jpeg_article')
  22. thumb_size = 250, 250
  23. def __init__(self, settings):
  24. super(JpegReader, self).__init__(settings)
  25. def read(self, source_path):
  26. try:
  27. if Exiv2Parser.get_exiv2_version() is not None:
  28. content, metadata = self.parse_jpeg(source_path=source_path)
  29. except ValueError: # if file can't be parsed, ignore it
  30. pass
  31. else:
  32. return content, metadata
  33. def parse_jpeg(self, *, source_path: str) -> Tuple[str, dict]:
  34. JpegReader.logger.info(source_path)
  35. img = Image.open(source_path)
  36. image_data = Exiv2Parser.get_values(source_path)
  37. title = image_data.get(Exiv.DESCRIPTION.value, 'Untitled')
  38. author = image_data.get(Exiv.ARTIST.value, 'Unknown')
  39. date_string = image_data.get(Exiv.DATETIME.value, '')
  40. date = datetime.strptime(date_string, "%Y:%m:%d %H:%M:%S")
  41. slug = URLWrapper(image_data.get(Exiv.HEADLINE.value, title), self.settings).slug
  42. description_long = image_data.get(Exiv.COMMENT.value, '')
  43. summary = image_data.get(Exiv.CAPTION.value, description_long[:140])
  44. tags = [Tag(tag, self.settings) for tag in image_data.get(Exiv.KEYWORDS.value, list())]
  45. content_root = self.settings[PelicanConfig.PATH.value]
  46. path_output = self.settings[PelicanConfig.OUTPUT_PATH.value]
  47. relative_source = dirname(source_path[len(content_root):]).lstrip(sep)
  48. if self.settings[PelicanConfig.USE_FOLDER_AS_CATEGORY.value]:
  49. category = relative_source.split(sep)[-1]
  50. else:
  51. category = image_data.get(Exiv.CATEGORY.value, None)
  52. type_of_content = relative_source.split(sep)[0] # either 'blog' or 'pages' as far as I know.
  53. url_site = self.settings[PelicanConfig.SITE_URL.value]
  54. if type_of_content.lower() == PelicanClass.PAGES.value:
  55. url_document = self.settings[PelicanConfig.PAGE_URL.value]
  56. document_save_as = self.settings[PelicanConfig.PAGE_SAVE_AS.value]
  57. else: # Assume PelicanClass.BLOG
  58. url_document = self.settings[PelicanConfig.ARTICLE_URL.value]
  59. document_save_as = self.settings[PelicanConfig.ARTICLE_SAVE_AS.value]
  60. page_url_complete = join(url_site, url_document)
  61. author_wrapper = Author(author, self.settings)
  62. # Move image in place:
  63. metadata = {PelicanMetadata.TITLE.value: title, PelicanMetadata.AUTHORS.value: [author_wrapper],
  64. PelicanMetadata.DATE.value: date, PelicanMetadata.SLUG.value: slug,
  65. PelicanMetadata.TAGS.value: tags,
  66. PelicanMetadata.CUSTOM_ALL.value: image_data}
  67. if category is not None:
  68. metadata[PelicanMetadata.CATEGORY.value] = Category(category, self.settings)
  69. thumb_name = '{0}_thumb.jpg'.format(slug)
  70. original_name = '{0}.jpg'.format(slug)
  71. path_output_html = join(path_output, document_save_as).format(**metadata)
  72. path_output_dir = dirname(path_output_html)
  73. path_output_original = join(path_output_dir, original_name)
  74. path_output_thumb = join(path_output_dir, thumb_name)
  75. # Here we generate the summary info incase this is used for articles we get nice thumbnails and summary
  76. metadata[PelicanMetadata.SUMMARY.value] = summary
  77. metadata[PelicanMetadata.FEATURED_IMAGE.value] = join(url_site, path_output_thumb[len(path_output):])
  78. if Exiv.OBJECT_NAME.value in image_data:
  79. metadata[PelicanMetadata.TEMPLATE.value] = image_data[Exiv.OBJECT_NAME.value]
  80. # Write the size/HTML out before we reduce the image to a thumb
  81. content = "<img src='{src}' alt='{alt}' style='width: {width}px; height: auto; max-width: 100%;'></img><p>{body}</p>" \
  82. .format(src=original_name, alt=title, width=img.width, height=img.height, body=description_long)
  83. # Ensure the directory levels exist
  84. if not isdir(path_output_dir):
  85. makedirs(path_output_dir)
  86. img.save(path_output_original)
  87. img.thumbnail(self.thumb_size)
  88. img.save(path_output_thumb)
  89. # Debug info if we need it
  90. JpegReader.logger.debug(content)
  91. JpegReader.logger.debug(str(metadata))
  92. JpegReader.logger.debug(path_output_html)
  93. return content, metadata
  94. def add_reader(readers):
  95. readers.reader_classes['jpeg_article'] = JpegReader
  96. def register():
  97. signals.readers_init.connect(add_reader)