build_image_cache.py 4.8 KB

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