build_image_cache.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. from dotenv import load_dotenv
  2. load_dotenv(".env")
  3. import logging
  4. import os
  5. import time
  6. from multiprocessing import Pool
  7. from pathlib import Path
  8. import cv2
  9. import typer
  10. from app.parser import get_files_by_extension
  11. from app.read_settings import read_settings
  12. logger = logging.getLogger(__name__)
  13. app = typer.Typer()
  14. # TODO this can be done better!
  15. # we're re-reading the git-repo path instead of assuming
  16. # we're currently in there, though empirical testing shows
  17. # doign Path.cwd() returns git repo path
  18. cwd = Path(__file__).parent
  19. settings = read_settings(f"{cwd}/settings.toml")
  20. git_repo = Path(settings.git_repo).expanduser()
  21. IMAGE_CACHE_PATH = f"{cwd}/static/images/cache"
  22. SIZES = {
  23. ".": [
  24. (320,),
  25. (640,),
  26. (760,),
  27. (1024,),
  28. (1366,),
  29. (1600,),
  30. (1920,),
  31. (2400,),
  32. (2880,),
  33. (3840,),
  34. ],
  35. "products": [
  36. (120, 120),
  37. (240, 240), # -- checkout
  38. (220, 220),
  39. (440, 440),
  40. (320,),
  41. (640,), # - product-grid
  42. (380,),
  43. (760,),
  44. (256,),
  45. (512,),
  46. (340,),
  47. (683,),
  48. (400,),
  49. (800,),
  50. (480,),
  51. (960,),
  52. (460,),
  53. (920,),
  54. (560,),
  55. (1120,),
  56. (1024,), # -- product specific sizes
  57. (1200,),
  58. (1366,),
  59. (1920,),
  60. (2400,),
  61. ],
  62. "pages": [(320,), (640,), (760,), (1024,), (1366,), (1600,), (1920,), (2400,)],
  63. }
  64. def calculate_height_on_width(img, input_width: int) -> int:
  65. """Calculate thumbnail's height value of given image by using the
  66. desired width.
  67. """
  68. width_percent = input_width / float(img.shape[1])
  69. return int((float(img.shape[0]) * float(width_percent)))
  70. def make_thumb(size_type: str, img, fp: os.PathLike):
  71. """Make a thumbnail image for each defined size."""
  72. try:
  73. new_filepath = Path(f"{fp.parent.stem}__{fp.stem}_{size_type[0]}{fp.suffix}")
  74. if Path(f"{IMAGE_CACHE_PATH}/{new_filepath}").exists():
  75. pass
  76. else:
  77. # check if size_type is a full tuple or is missing the
  78. # height value
  79. if len(size_type) == 2:
  80. img = cv2.resize(img, size_type)
  81. else:
  82. img_height = calculate_height_on_width(img, size_type[0])
  83. img = cv2.resize(img, (size_type[0], img_height))
  84. # save it to disk
  85. cv2.imwrite(f"{IMAGE_CACHE_PATH}/{new_filepath}", img)
  86. except OSError as e:
  87. logging.error(f"image-resize error => {e}")
  88. def image_resize(image: tuple[str, str]):
  89. """Resize an image at given preset, if it does not exist yet.
  90. We're saving each thumbnail in static/images/cache/,
  91. in the format /<pa/th>/<image>_<size>.<ext>
  92. """
  93. template, filepath = image
  94. fp = Path(filepath)
  95. selected_size = SIZES[template]
  96. # read image and keep it in memory
  97. img = cv2.imread(f"{git_repo}/{filepath}")
  98. # copy also image as is to the cache
  99. copy_filepath = Path(f"{fp.parent.stem}__{fp.stem}_{img.shape[1]}{fp.suffix}")
  100. if not Path(f"{IMAGE_CACHE_PATH}/{copy_filepath}").exists():
  101. cv2.imwrite(f"{IMAGE_CACHE_PATH}/{copy_filepath}", img)
  102. for size_type in selected_size:
  103. if img.shape[1] > size_type[0]:
  104. make_thumb(size_type, img, fp)
  105. @app.command()
  106. def build_all_images(repo: str):
  107. """Build set of defined thumbnails for each image in the list.
  108. Ref: <https://stackoverflow.com/a/51822265>
  109. """
  110. pool = Pool(int(os.getenv("CPU_CORE")))
  111. repo = Path(repo).expanduser()
  112. pattern_matching = ["*.png", "*.jpg"]
  113. filepaths = get_files_by_extension(repo, pattern_matching, relative_path=True)
  114. images = [
  115. (str(Path(filepath).parent).split("/")[0], filepath) for filepath in filepaths
  116. ]
  117. if len(images) > 0:
  118. start_time = time.time()
  119. pool.map(image_resize, images)
  120. logging.info("--- %s seconds ---" % (time.time() - start_time))
  121. else:
  122. logging.info("build-images: no images in the repo.")
  123. @app.command()
  124. def build_images(repo: str, oldrev: str, newrev: str):
  125. """Build set of defined thumbnails for each image in the list.
  126. Ref: <https://stackoverflow.com/a/51822265>
  127. """
  128. pool = Pool(int(os.getenv("CPU_CORE")))
  129. repo = Path(repo).expanduser()
  130. pattern_matching = [".png", ".jpg"]
  131. filepaths = get_files_by_extension(repo, pattern_matching, relative_path=True)
  132. images = [
  133. (str(Path(filepath).parent).split("/")[0], filepath) for filepath in filepaths
  134. ]
  135. if len(images) > 0:
  136. start_time = time.time()
  137. pool.map(image_resize, images)
  138. logging.info("--- %s seconds ---" % (time.time() - start_time))
  139. else:
  140. logging.info("build-images: no image in this commit.")
  141. if __name__ == "__main__":
  142. app()