exhib.rkt 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #!/usr/bin/env racket
  2. #lang racket
  3. ; A static web gallery generator, done right!
  4. ; Copyright (C) 2017 Pelle Hjek
  5. ;
  6. ; This program is free software: you can redistribute it and/or modify
  7. ; it under the terms of the GNU Affero General Public License as published by
  8. ; the Free Software Foundation, either version 3 of the License, or
  9. ; (at your option) any later version.
  10. ;
  11. ; This program is distributed in the hope that it will be useful,
  12. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ; GNU Affero General Public License for more details.
  15. ;
  16. ; You should have received a copy of the GNU Affero General Public License
  17. ; along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. (require xml)
  19. (define usage
  20. "Usage: exhibition FOLDER"
  21. )
  22. (define (basename path)
  23. (let
  24. ((base
  25. (call-with-values
  26. (lambda () (split-path (simplify-path path)))
  27. (lambda (path relative root?) relative))))
  28. ;need to ask this because split-path may output 'same
  29. (path->string (if (eq? base 'same) path base))))
  30. (define (directory? path)
  31. (and
  32. (directory-exists? path)
  33. (not (regexp-match #px".git" path))))
  34. (define (image? path)
  35. (and
  36. (file-exists? path)
  37. ;only find image files
  38. (regexp-match #px".+\\.(?i:jpg|png|gif|bmp|tiff)$" path)))
  39. (define (text-file? path)
  40. (and
  41. (file-exists? path)
  42. ;only find image files
  43. (regexp-match #px".+\\.(?i:txt)$" path)))
  44. (define (thumb? path)
  45. (regexp-match #px"thumb.png$" path))
  46. (define (montage? path)
  47. (regexp-match #px"montage.png$" path))
  48. (define (dir-filter-list fn dir)
  49. (filter
  50. fn
  51. (map
  52. (lambda (content) (build-path dir content))
  53. (directory-list dir))))
  54. (define (discard what images)
  55. (filter
  56. (lambda (image) (not (what image))) images))
  57. (define (thumb! path)
  58. ;make a thumb of an image
  59. ; a) create a thumb (as a side-effect)
  60. ; b) return the thumbnail s-expr with link to the original
  61. (let
  62. ((thumb (string-append (path->string path) ".thumb.png")))
  63. ;don't redo the thumb if it's already there
  64. (if (not (file-exists? thumb))
  65. (system*
  66. (find-executable-path "convert")
  67. ; "-thumbnail" "x200"
  68. "-gravity" "center"
  69. "-resize" "200^>x200^>"
  70. "-crop" "200x200+0+0"
  71. ;imagemagick needs the FULL path
  72. (path->string path)
  73. thumb)
  74. #f)
  75. ;the link in the HTML should just be RELATIVE to the current folder
  76. `(span
  77. (a ((href ,(basename path)))
  78. (img
  79. ((src ,(basename thumb))
  80. (title ,(basename path))
  81. (alt ,(basename path))
  82. (class "image")))))))
  83. (define (montage! path)
  84. ;do a 4x4 montage of a directory
  85. ;it's alright to redo the montages every time
  86. (let
  87. (
  88. ;the thumb should not be IN the folder it is of but OUTSIDE to enable recursive montages
  89. (montage (string-append (path->string path) ".montage.png"))
  90. (these-images (discard thumb? (dir-filter-list image? path)))
  91. )
  92. (apply system*
  93. (flatten
  94. `(,(find-executable-path "montage")
  95. ;these images may include montages, to make montage of thumbs
  96. ;but not thumbs because then the images go in there twice
  97. ,(take
  98. (map path->string these-images)
  99. (min 4 (length these-images)))
  100. "-background" "transparent"
  101. "-gravity" "Center"
  102. ; "-resize" "100x100"
  103. "-resize" "100^>x100^>"
  104. "-crop" "100x100+0+0"
  105. "-geometry" "100x100"
  106. "-tile" "2x2"
  107. ; "-title" ,(basename path)
  108. ,montage)))
  109. `(span (a ((href ,(string-append (basename path) "/index.html"))
  110. (style "display:inline-block;"))
  111. (img
  112. ((src ,(basename montage))
  113. (title ,(basename path))
  114. (alt ,(basename path))
  115. (class "folder")
  116. ))
  117. (div ((style "font-size: smaller; height: 3em; width: 200; word-wrap: break-word; overflow: scroll;")) ,(basename path))))
  118. ))
  119. (define (index! path)
  120. (map index! (dir-filter-list directory? path))
  121. (display-to-file
  122. (xexpr->string
  123. `(html
  124. (head (title ,(basename path))
  125. (link ((rel "stylesheet") (type "text/css") (href "http://lizdorton.tk/liz.css"))))
  126. (style "img {filter:brightness(0.7);transition-duration:0.5s}")
  127. (style "img:hover {filter:brightness(1.1);transition-duration:0s;border:1px solid white;}")
  128. (style ".image {border:1px solid gray;margin:2px;}")
  129. (style ".folder {border:1px solid red;margin:2px;}")
  130. (style "body {margin:0;margin-top:8em;text-align:center;}")
  131. (body
  132. (div
  133. ((style "background:white;color:black;position:fixed;top:0;z-index:10;width:100vw;"))
  134. (h1
  135. (a ((href "../index.html")) "..") " "
  136. ,(basename path)))
  137. ,(cons 'div
  138. (map (lambda (s) (list 'div s)) (map file->string
  139. (dir-filter-list text-file? path))))
  140. ,(cons 'div (map montage! (dir-filter-list directory? path)))
  141. ;do not make thumbs of thumbnails here
  142. ,(cons 'div (map thumb! (discard montage? (discard thumb? (dir-filter-list image? path)))))
  143. )))
  144. (string-append (path->string path) "/index.html") #:exists 'replace))
  145. (define (main)
  146. (if (< 0 (vector-length (current-command-line-arguments)))
  147. ;run it if there's a folder given
  148. (time (vector-map index!
  149. (vector-map string->path
  150. (current-command-line-arguments)))
  151. "Done!")
  152. ;otherwise display usage
  153. usage))
  154. ;(discard montage? (images "/home/pelle/example/stuff"))
  155. (main)