Serve images by mtime using Snap

posted on 2015-09-25

I've started playing around with a security camera, simply storing images using motion for now.

This will take a snapshot any time motion is detected. To limit the disk usage, files are named based on the time of day only and the image contains the exact timestamp:

text_right %Y-%m-%d\n%T-%q
jpeg_filename %H/%M/%S_%q

I decided to create a simple webpage to allow me to review the images, latest on top. I can still find the latest image by using the mtime (modification time) on the filesystem.

The complete project code is hosted on github as a cabal project with the Snap web framework as a depedency. The complete code is only a single page long and does the following simple things.

Get the modification time of a file (as an integer, as we only use it for sorting):

getMTimeInteger :: FilePath -> IO Int
getMTimeInteger filepath = do
    status <- getFileStatus filepath
    let mtime = modificationTime status
    return (fromEnum mtime)

and create a list of files with their modification time in a tuple list

listImagesWithCtime :: IO [(Int, FilePath)]
listImagesWithCtime = do
    files <- find always (extension ==? ".jpg" ||? extension ==? ".png") imageHostingBasePath
    mtimeFilenameTuplesOf files imageHostingBasePath

and then just write the list out as a JSON encoded list

imageIndexHandler :: Snap ()
imageIndexHandler = do
        listing <- liftIO listImagesWithCtime
        writeJSON (listing :: [(Int, FilePath)])

this allows for the front-end to load the files, sort by mtime, and finally lazy load them using the jquery-unveil plugin:

$.each(fileListing, function addImage(idx, imageInformation){
    var image = $("<img />"),
        link = $("<a />"),
        imageLocation = "images/" + imageInformation[1];
    image.attr("src", "template.jpg");
    image.attr("data-src", imageLocation);
    link.attr("href", imageLocation);


The end result? A long list of images, the bottom list of which does not load until you scroll down to see them:

Screenshot of the result

If you want to give it a try, clone the repository and get to the point where you can call cabal run. Happy hacking!