aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/FileProcessors.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/FileProcessors.hs')
-rw-r--r--compiler/src/FileProcessors.hs146
1 files changed, 146 insertions, 0 deletions
diff --git a/compiler/src/FileProcessors.hs b/compiler/src/FileProcessors.hs
new file mode 100644
index 0000000..78e7351
--- /dev/null
+++ b/compiler/src/FileProcessors.hs
@@ -0,0 +1,146 @@
1-- ldgallery - A static generator which turns a collection of tagged
2-- pictures into a searchable web gallery.
3--
4-- Copyright (C) 2019-2022 Pacien TRAN-GIRARD
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
8-- published by the Free Software Foundation, either version 3 of the
9-- License, or (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
19module FileProcessors
20 ( FileProcessor
21 , transformThenDescribe
22 , copyResource
23 , noopProcessor
24 , FileTransformer
25 , copyFileProcessor
26 , resizePictureUpTo
27 , resourceAt
28 , getImageResolution
29 , FileDescriber
30 , getResProps
31 , getPictureProps
32 , getThumbnailProps
33 ) where
34
35
36import Control.Exception (Exception, throwIO)
37import System.Process (readProcess, callProcess)
38import Text.Read (readMaybe)
39
40import System.Directory (getModificationTime)
41import qualified System.Directory
42
43import Config (Resolution(..))
44import Resource (Resource(..), GalleryItemProps(..), Thumbnail(..))
45import Files
46
47
48data ProcessingException = ProcessingException FilePath String deriving Show
49instance Exception ProcessingException
50
51type FileProcessor a =
52 Path -- ^ Item path
53 -> Path -- ^ Target resource path
54 -> FilePath -- ^ Filesystem input path
55 -> FilePath -- ^ Filesystem output path
56 -> IO a
57
58transformThenDescribe :: FileTransformer -> FileDescriber a -> FileProcessor a
59transformThenDescribe transformer describer _itemPath resPath fsInPath fsOutPath =
60 transformer fsInPath fsOutPath >> describer resPath fsOutPath
61
62copyResource :: (Resource -> a) -> FileProcessor a
63copyResource resPropConstructor =
64 transformThenDescribe copyFileProcessor (getResProps resPropConstructor)
65
66noopProcessor :: FileProcessor (Maybe a)
67noopProcessor _ _ _ _ = return Nothing
68
69
70type FileTransformer =
71 FileName -- ^ Input path
72 -> FileName -- ^ Output path
73 -> IO ()
74
75copyFileProcessor :: FileTransformer
76copyFileProcessor inputPath outputPath =
77 putStrLn ("Copying:\t" ++ outputPath)
78 >> ensureParentDir (flip System.Directory.copyFile) outputPath inputPath
79
80resizePictureUpTo :: Resolution -> FileTransformer
81resizePictureUpTo maxResolution inputPath outputPath =
82 putStrLn ("Processing:\t" ++ outputPath)
83 >> ensureParentDir (flip resize) outputPath inputPath
84 where
85 maxSize :: Resolution -> String
86 maxSize Resolution{width, height} = show width ++ "x" ++ show height ++ ">"
87
88 resize :: FileName -> FileName -> IO ()
89 resize input output = callProcess "magick"
90 [ input
91 , "-auto-orient"
92 , "-resize", maxSize maxResolution
93 , output ]
94
95
96type FileDescriber a =
97 Path -- ^ Target resource path
98 -> FilePath -- ^ Filesystem path
99 -> IO a
100
101getImageResolution :: FilePath -> IO Resolution
102getImageResolution fsPath =
103 readProcess "magick"
104 [ "identify"
105 , "-ping"
106 , "-format", "%[orientation] %w %h"
107 , firstFrame
108 ] []
109 >>= parseOutput . words
110
111 where
112 firstFrame :: FilePath
113 firstFrame = fsPath ++ "[0]"
114
115 -- Flip the dimensions when necessary according to the metadata.
116 -- ImageMagick's `-auto-orient` flag does the same, but isn't compatible
117 -- with `-ping` and causes the whole image file to be loaded.
118 parseOutput :: [String] -> IO Resolution
119 parseOutput ["RightTop", w, h] = parseResolution (h, w)
120 parseOutput ["LeftBottom", w, h] = parseResolution (h, w)
121 parseOutput [_, w, h] = parseResolution (w, h)
122 parseOutput _ = throwIO failedRead
123
124 parseResolution :: (String, String) -> IO Resolution
125 parseResolution (widthString, heightString) =
126 case (readMaybe widthString, readMaybe heightString) of
127 (Just w, Just h) -> return $ Resolution w h
128 _ -> throwIO failedRead
129
130 failedRead :: ProcessingException
131 failedRead = ProcessingException fsPath "Unable to read image resolution."
132
133resourceAt :: FileDescriber Resource
134resourceAt resPath fsPath = Resource resPath <$> getModificationTime fsPath
135
136getResProps :: (Resource -> a) -> FileDescriber a
137getResProps resPropsConstructor resPath fsPath =
138 resPropsConstructor <$> resourceAt resPath fsPath
139
140getPictureProps :: FileDescriber GalleryItemProps
141getPictureProps resPath fsPath =
142 Picture <$> resourceAt resPath fsPath <*> getImageResolution fsPath
143
144getThumbnailProps :: FileDescriber (Maybe Thumbnail)
145getThumbnailProps resPath fsPath =
146 Just <$> (Thumbnail <$> resourceAt resPath fsPath <*> getImageResolution fsPath)