ctucx.git: gallery

static-site-generator for image-galleries [used @ photos.ctu.cx]

commit 2411e91fb1d6315646c5fbe071e685438f6f4f96
parent 2024ea58c47dba964a908569e31bd975cf4b8224
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 11 Mar 2021 00:28:51 +0100

src/gallery.nim: refactor code, allow dynamic width/height for thumbnails
5 files changed, 250 insertions(+), 132 deletions(-)
M
sample.config
|
24
++++++++++++------------
M
src/assets/album.html
|
40
++++++++++++++++++----------------------
M
src/assets/picture.html
|
20
++++++++++----------
M
src/assets/style.css
|
2
--
M
src/gallery.nim
|
296
++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
diff --git a/sample.config b/sample.config
@@ -1,18 +1,18 @@
-SourceDir=/home/ctucx/Pictures/Bahnbilder
+SourceDir=./foobar
 TargetDir=./out
 
 [Site]
-Author=ctucx
-Name="ctucx' sample gallery"
+Author=Max Mustermann
+Name="ctucx gallery sample"
 Description="a short discription for your site"
-Tags="a list of tags for seo stuff" 
-ShowOriginalsButton=false
+Tags="a list of tags for seo stuff"
+ShowOriginalsButton=true
 SymlinkOriginals=false
-EnableJS=true   ;if disabled no exif data will get parsed and keyboard navigation does not work
+EnableJS=true ;if disabled no exif data will get parsed and keyboard navigation does not work
 
-[Thumbnail]
-MediumMaxWidth=1920
-MediumMaxHeight=1080
-ThumbMaxWidth=200
-ThumbMaxHeight=200
-ThumbQuality=90
+[Thumbnails]
+; if u set only one of these, it will generate thumbnails with the given width/height while keeping
+; the aspect-ratio of the original photo. but currently this breaks the css and it looks ugly.
+SmallWidth=200
+SmallHeight=200
+SmallQuality=90
diff --git a/src/assets/album.html b/src/assets/album.html
@@ -3,11 +3,11 @@
 	<head>
 		<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
 
-		<title>{{name}} | {{SiteName}}</title>
+		<title>{{name}} | {{siteName}}</title>
 
-		<meta name="description" content="{{SiteDescription}}">
-		<meta name="keywords" content="{{SiteTags}}">
-		<meta name="author" content="{{SiteAuthor}}">
+		<meta name="description" content="{{siteDescription}}">
+		<meta name="keywords" content="{{siteTags}}">
+		<meta name="author" content="{{siteAuthor}}">
 
         <meta name="generator" content="https://git.ctu.cx/ctucx/gallery">
 		<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0">

@@ -85,38 +85,34 @@
 		</header>
 
 		<div class="content contentZoomIn">
-			{{#hasSubalbums}}
-			{{#isSubalbum}}
+			{{#showDivider}}
 			<div class="divider">
 				<h1>Albums</h1>
 			</div>
-			{{/isSubalbum}}
+			{{/showDivider}}
 
 			{{#subalbums}}
-
-			<a href="{{name}}/" class="album">
-				<img src="./{{thumbnail1}}" alt="Photo thumbnail" width="200" height="200">
-				<img src="./{{thumbnail2}}" alt="Photo thumbnail" width="200" height="200">
-				<img src="./{{thumbnail3}}" alt="Photo thumbnail" width="200" height="200">
-				<span class="overlay">
+			<a href="{{name}}/" class="album" style="width: {{thumbnail3w_css}}px; height: {{thumbnail3h_css}}px;">
+				<img src="./{{thumbnail1}}" alt="Photo thumbnail" width="{{thumbnail1w}}" height="{{thumbnail1h}}">
+				<img src="./{{thumbnail2}}" alt="Photo thumbnail" width="{{thumbnail2w}}" height="{{thumbnail2h}}">
+				<img src="./{{thumbnail3}}" alt="Photo thumbnail" width="{{thumbnail3w}}" height="{{thumbnail3h}}">
+				<span class="overlay" style="width: {{thumbnail1w_css}}px;">
 					<h1>{{name}}</h1>
 					<p>{{numPictures}} Pictures - {{numAlbums}} Albums</p>
 				</span>
 			</a>
 			{{/subalbums}}
 
-			{{#isSubalbum}}
+			{{#showDivider}}
 			<div class="divider">
 				<h1>Photos</h1>
 			</div>
-			{{/isSubalbum}}
-
-			{{/hasSubalbums}}
+			{{/showDivider}}
 
 			{{#pictures}}
-			<a href="{{name}}.html" class="photo">
-				<img src="thumbnails/{{name}}.png" alt="Photo thumbnail" width="{{ThumbThumbMaxWidth}}" height="{{ThumbThumbMaxHeight}}">
-				<span class="overlay">
+			<a href="{{name}}.html" class="photo" style="width: {{width_css}}px; height: {{height_css}}px;">
+				<img src="thumbnails/{{name}}.png" alt="Photo thumbnail" width="{{width}}" height="{{height}}">
+				<span class="overlay" style="width: {{width_css}}px;">
 					<h1>{{name}}</h1>
 					<!--<p><span title="Camera Date"><svg class="iconic "><use xlink:href="/iconic.svg#camera-slr"></use></svg></span></p>-->
 				</span>

@@ -125,14 +121,14 @@
 
 		</div>
 		{{#isSubalbum}}
-    	{{#SiteEnableJS}}
+    	{{#enableJS}}
 		<script type="text/javascript">
 			window.onkeyup = function(e) {
 				if (e.keyCode == 27) window.location = "..";
 				if (e.keyCode == 32) document.getElementById("toggle").checked = true;
 			}
 		</script>
-		{{/SiteEnableJS}}
+		{{/enableJS}}
 		{{/isSubalbum}}
 
 	</body>
diff --git a/src/assets/picture.html b/src/assets/picture.html
@@ -2,11 +2,11 @@
 <html>
 	<head>
 		<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
-		<title>{{name}} | {{SiteName}}</title>
+		<title>{{name}} | {{siteName}}</title>
 
-		<meta name="description" content="{{SiteDescription}}">
-		<meta name="keywords" content="{{SiteTags}}">
-		<meta name="author" content="{{SiteAuthor}}">
+		<meta name="description" content="{{siteDescription}}">
+		<meta name="keywords" content="{{siteTags}}">
+		<meta name="author" content="{{siteAuthor}}">
 
         <meta name="generator" content="https://git.ctu.cx/ctucx/gallery">
 		<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0">

@@ -23,9 +23,9 @@
 			<a class="button" href="./" id="button_close" title="Close Photo"><svg class="iconic"><use xlink:href="/iconic.svg#chevron-left"></use></svg></a>
 			<a class="header-title">{{name}}<svg class="iconic "><use xlink:href="/iconic.svg#caret-bottom"></use></svg></a>
 
-			{{#SiteShowOrigBtn}}
+			{{#showOriginalsButton}}
 			<a class="button" href="{{orig}}" title="Download"><svg class="iconic"><use xlink:href="/iconic.svg#cloud-download"></use></svg></a>
-			{{/SiteShowOrigBtn}}
+			{{/showOriginalsButton}}
 			
 			<a class="header-divider"></a>
 

@@ -130,11 +130,11 @@
 							</tr>
 						</tbody>
 					</table>
-					{{^SiteEnableJS}}
+					{{^enableJS}}
 					<p>
 					Exif parsing is disabled by owner of this site.
 					</p>
-					{{/SiteEnableJS}}
+					{{/enableJS}}
 				</div>
 			</div>
 		</header>

@@ -158,7 +158,7 @@
 			</div>
 			{{/hasNext}}
 		</div>
-		{{#SiteEnableJS}}
+		{{#enableJS}}
 		<script type="text/javascript" src="/exif.js"></script>
 		<script type="text/javascript">
 			window.onload=getExif;

@@ -209,6 +209,6 @@
 	    		});
 			}
 		</script>
-		{{/SiteEnableJS}}
+		{{/enableJS}}
 	</body>
 </html>
diff --git a/src/assets/style.css b/src/assets/style.css
@@ -215,8 +215,6 @@ input {
 .content .album img,
 .content .photo img {
   position: absolute;
-  width: 200px;
-  height: 200px;
   background: #222;
   color: #222;
   box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
diff --git a/src/gallery.nim b/src/gallery.nim
@@ -1,14 +1,23 @@
 import os, osproc, options, json, strutils, random, algorithm, parsecfg
 import moustachu
 
-const asset_exif_js      = staticRead"./assets/exif.js"
-const asset_style_css    = staticRead"./assets/style.css"
-const asset_noimages_svg = staticRead"./assets/no_images.svg"
-const asset_iconic_svg   = staticRead"./assets/iconic.svg"
-const asset_album_html   = staticRead"./assets/album.html"
-const asset_picture_html = staticRead"./assets/picture.html"
-
 type
+    Config* = object
+        sourceDir*:           string
+        targetDir*:           string
+        siteName*:            string
+        siteAuthor*:          string
+        siteTags*:            string
+        siteDescription*:     string
+        showOriginalsButton*: bool
+        symlinkOriginals*:    bool
+        enableJS*:            bool
+        thumbMediumWidth*:    int
+        thumbMediumHeight*:   int
+        thumbSmallWidth*:     int
+        thumbSmallHeight*:    int
+        thumbSmallQuality*:   int
+
     Album* = object
         name*:       string
         path*:       string

@@ -18,12 +27,24 @@ type
         pictures*:   seq[Picture]
 
     Picture* = object
-        name*:     string
-        path*:     string
-        filename*: string
-        filetype*: string
-        desc*:     Option[string]
-        size*:     BiggestInt
+        name*:        string
+        path*:        string
+        width*:       int
+        height*:      int
+        thumbWidth*:  int
+        thumbHeight*: int
+        filename*:    string
+        filetype*:    string
+        filesize*:    BiggestInt
+        desc*:        Option[string]
+
+const asset_exif_js      = staticRead"./assets/exif.js"
+const asset_style_css    = staticRead"./assets/style.css"
+const asset_noimages_svg = staticRead"./assets/no_images.svg"
+const asset_iconic_svg   = staticRead"./assets/iconic.svg"
+const asset_album_html   = staticRead"./assets/album.html"
+const asset_picture_html = staticRead"./assets/picture.html"
+var   config* {.threadvar.}: Config
 
 ###
 #

@@ -59,17 +80,38 @@ proc createPicture(path: string): Picture =
 
     if fileExists(joinPath(dir, name, ".txt")): result.desc = some(readFile(joinPath(dir, name,".txt")))
 
-    result.name       = name
-    result.path       = dir
-    result.filename   = lastPathPart(path)
-    result.filetype   = ext.replace(".", "")
-    result.size       = getFileSize(path)
+    let size = execProcess("/usr/bin/identify", args=[
+        "-ping",
+        "-format", "'%w %h'",
+        quoteShell(path)
+    ], options={poUsePath}).replace("\n", "").replace("'", "").split(" ")
+
+    result.width       = parseInt(size[0])
+    result.height      = parseInt(size[1])
+
+    if config.thumbSmallWidth == 0:
+        result.thumbWidth  = toInt(toFloat(result.width) / (result.height / config.thumbSmallHeight))
+        result.thumbHeight = config.thumbSmallHeight
+
+    elif config.thumbSmallHeight == 0:
+        result.thumbWidth  = config.thumbSmallWidth
+        result.thumbHeight = toInt(toFloat(result.height) / (result.width / config.thumbSmallWidth))
+
+    else:
+        result.thumbWidth  = config.thumbSmallWidth
+        result.thumbHeight = config.thumbSmallHeight
+
+    result.name        = name
+    result.path        = dir
+    result.filename    = lastPathPart(path)
+    result.filetype    = ext.replace(".", "")
+    result.filesize    = getFileSize(path)
 
 
 proc createAlbum(path: string, isRoot: bool): Album = 
     result.name    = lastPathPart(path)
 
-    if isRoot != false:
+    if not isRoot:
         result.path       = path
 
     if not fileExists(joinPath(path, ".nomedia")): result.visible = true

@@ -98,9 +140,9 @@ proc placeAssets(targetDir: string, enableJS: bool) =
     if enableJS: writeFile(joinPath(targetDir, "exif.js"), asset_exif_js)
 
 
-proc removeOrphans (targetDir: string, config: JsonNode) = 
+proc removeOrphans (targetDir: string) = 
     echo "Checking for orphaned files and folders..."
-    let sourceDir = joinPath(config["SourceDir"].getStr, targetDir.replace(config["TargetDir"].getStr, ""))
+    let sourceDir = joinPath(config.sourceDir, targetDir.replace(config.targetDir, ""))
 
     #Albums
     for album in walkDir(targetDir):

@@ -130,7 +172,7 @@ proc removeOrphans (targetDir: string, config: JsonNode) =
             removeFile(joinPath(targetDir, "thumbnails", name & ".png"))
 
 
-proc generateWebsite(targetDir: string, album: Album, config: JsonNode) =
+proc generateWebsite(targetDir: string, album: Album) =
     echo "============"
     echo "Create Album:" & album.name
     discard existsOrCreateDir(targetDir)

@@ -143,49 +185,87 @@ proc generateWebsite(targetDir: string, album: Album, config: JsonNode) =
         "numAlbums":    album.subalbums.len,
         "numPictures":  album.pictures.len,
         "isSubalbum":   true,
-        "hasSubalbums": false,
+        "showDivider":  false,
         "subalbums":    [],
         "pictures":     []
-    }, config)
+    }, %config)
 
     var smallThumbnails  = newSeq[string]()
     var mediumThumbnails = newSeq[string]()
 
-    if album.path != "": templateContext["isSubalbum"]          = %false
-    if not album.desc.isNone: templateContext["description"]    = %album.desc.get
-    if album.subalbums.len > 0: templateContext["hasSubalbums"] = %true
+    if album.path == "": templateContext["isSubalbum"]          = %false
+    if album.subalbums.len > 0 and album.pictures.len > 0: templateContext["showDivider"] = %true
+    if not album.desc.isNone: templateContext["description"]  = %album.desc.get
 
     for subalbum in album.subalbums:
-        generateWebsite(joinPath(targetDir, subalbum.name), subalbum, config)
+        generateWebsite(joinPath(targetDir, subalbum.name), subalbum)
+
+        var thumbnail1      = "/no_images.svg"
+        var thumbnail1w     = 200
+        var thumbnail1h     = 200
 
-        var thumbnail1 = "/no_images.svg"
-        var thumbnail2 = "/no_images.svg"
-        var thumbnail3 = "/no_images.svg"
+        var thumbnail2      = "/no_images.svg"
+        var thumbnail2w     = 200
+        var thumbnail2h     = 200
+
+        var thumbnail3      = "/no_images.svg"
+        var thumbnail3w     = 200
+        var thumbnail3h     = 200
+        var thumbnail3w_css = 202
+        var thumbnail3h_css = 202
 
         if subalbum.pictures.len > 0:
-            thumbnail1 = subalbum.name & "/thumbnails/" & subalbum.pictures[rand(0..subalbum.pictures.len-1)].name & ".png"
-            thumbnail2 = subalbum.name & "/thumbnails/" & subalbum.pictures[rand(0..subalbum.pictures.len-1)].name & ".png"
-            thumbnail3 = subalbum.name & "/thumbnails/" & subalbum.pictures[rand(0..subalbum.pictures.len-1)].name & ".png"
+            let pic1 = rand(0..subalbum.pictures.len-1)
+            let pic2 = rand(0..subalbum.pictures.len-1)
+            let pic3 = rand(0..subalbum.pictures.len-1)
+
+            thumbnail1      = subalbum.name & "/thumbnails/" & subalbum.pictures[pic1].name & ".png"
+            thumbnail1w     = subalbum.pictures[pic1].thumbWidth
+            thumbnail1h     = subalbum.pictures[pic1].thumbHeight
+
+            thumbnail2 = subalbum.name & "/thumbnails/" & subalbum.pictures[pic2].name & ".png"
+            thumbnail2w     = subalbum.pictures[pic2].thumbWidth
+            thumbnail2h     = subalbum.pictures[pic2].thumbHeight
+
+            thumbnail3 = subalbum.name & "/thumbnails/" & subalbum.pictures[pic3].name & ".png"
+            thumbnail3w     = subalbum.pictures[pic3].thumbWidth
+            thumbnail3h     = subalbum.pictures[pic3].thumbHeight
+            thumbnail3w_css = subalbum.pictures[pic3].thumbWidth + 2
+            thumbnail3h_css = subalbum.pictures[pic3].thumbHeight + 2
 
         templateContext["subalbums"].add(%* {
-            "name":        subalbum.name,
-            "numAlbums":   subalbum.subalbums.len,
-            "numPictures": subalbum.pictures.len,
-            "thumbnail1":  thumbnail1,
-            "thumbnail2":  thumbnail2,
-            "thumbnail3":  thumbnail3
+            "name":            subalbum.name,
+            "numAlbums":       subalbum.subalbums.len,
+            "numPictures":     subalbum.pictures.len,
+
+            "thumbnail1":      thumbnail1,
+            "thumbnail1w":     thumbnail1w,
+            "thumbnail1h":     thumbnail1h,
+
+            "thumbnail2":      thumbnail2,
+            "thumbnail2w":     thumbnail2w,
+            "thumbnail2h":     thumbnail2h,
+
+            "thumbnail3":      thumbnail3,
+            "thumbnail3w":     thumbnail3w,
+            "thumbnail3h":     thumbnail3h,
+            "thumbnail3w_css": thumbnail3w_css,
+            "thumbnail3h_css": thumbnail3h_css
         })
 
     for index, picture in album.pictures:
+        var width, height: int
         var pictureTemplateContext = mergeJson(%* {
             "name":        picture.name,
-            "orig":        joinPath("/originals", picture.path.replace(config["SourceDir"].getStr, ""), picture.filename),
+            "orig":        joinPath("/originals", picture.path.replace(config.sourceDir, ""), picture.filename),
             "filename":    picture.filename,
+            "width":       picture.width,
+            "height":      picture.height,
             "description": "-",
             "hasPrev":     false,
             "hasNext":     false,
-            "size":        (picture.size.int/1000/1000)
-        }, config)
+            "filesize":    (picture.filesize.int/1000/1000)
+        }, %config)
 
         if not picture.desc.isNone: pictureTemplateContext["description"] = %picture.desc.get
 

@@ -202,13 +282,53 @@ proc generateWebsite(targetDir: string, album: Album, config: JsonNode) =
         writeFile(joinPath(targetDir, picture.name & ".html"), render(asset_picture_html, pictureTemplateContext))
 
         if not fileExists(joinPath(targetDir, "thumbnails", picture.name & ".png")):
-            smallThumbnails.add("/usr/bin/env mogrify -strip -quality " & $config["ThumbThumbQuality"].getInt & " -format png -path " & quoteShell(joinPath(targetDir, "thumbnails")) & " -thumbnail " & $config["ThumbThumbMaxWidth"].getInt & "x" & $config["ThumbThumbMaxHeight"].getInt & "^ -gravity center -extent " & $config["ThumbThumbMaxWidth"].getInt & "x" & $config["ThumbThumbMaxHeight"].getInt & " " & quoteShell(joinPath(picture.path, picture.filename)))
+            if config.thumbSmallWidth == 0:
+                smallThumbnails.add(["/usr/bin/env mogrify",
+                    "-quality", $config.thumbSmallQuality,
+                    "-format", "png",
+                    "-path", quoteShell(joinPath(targetDir, "thumbnails")),
+                    "-thumbnail", "x" & $config.thumbSmallHeight,
+                    quoteShell(joinPath(picture.path, picture.filename))
+                ].join(" "))
+
+            elif config.thumbSmallHeight == 0:
+                smallThumbnails.add(["/usr/bin/env mogrify",
+                    "-strip",
+                    "-quality", $config.thumbSmallQuality,
+                    "-format", "png",
+                    "-path", quoteShell(joinPath(targetDir, "thumbnails")),
+                    "-thumbnail", $config.thumbSmallHeight & "x",
+                    quoteShell(joinPath(picture.path, picture.filename))
+                ].join(" "))
+
+            else:
+                if not fileExists(joinPath(targetDir, "thumbnails", picture.name & ".png")):
+                    smallThumbnails.add(["/usr/bin/env mogrify",
+                        "-quality", $config.thumbSmallQuality,
+                        "-format", "png",
+                        "-path", quoteShell(joinPath(targetDir, "thumbnails")),
+                        "-thumbnail", $config.thumbSmallWidth & "x" & $config.thumbSmallHeight & "^",
+                        "-gravity", "center",
+                        "-extent", $config.thumbSmallWidth & "x" & $config.thumbSmallHeight,
+                        quoteShell(joinPath(picture.path, picture.filename))
+                    ].join(" "))
+
+        if not fileExists(joinPath(targetDir, "medium", picture.filename)):
+            echo "Generate medium thumbnail!"
+            discard execProcess("/usr/bin/mogrify", args=[
+                "-format", picture.filetype,
+                "-path", quoteShell(joinPath(targetDir, "medium")),
+                "-resize", $config.thumbMediumWidth & "x>",
+                quoteShell(joinPath(picture.path, picture.filename))
+            ], options={poUsePath})
 
-        if not fileExists(joinPath(targetDir, "medium", picture.name & "." & picture.filetype)):
-            mediumThumbnails.add("/usr/bin/env mogrify -format " & picture.filetype & " -path " & quoteShell(joinPath(targetDir, "medium")) & " -resize " & $config["ThumbMediumMaxWidth"].getInt & "x\\> " & quoteShell(joinPath(picture.path, picture.filename)))
 
         templateContext["pictures"].add(%* {
-            "name": picture.name,
+            "name":       picture.name,
+            "width":      picture.thumbWidth,
+            "height":     picture.thumbHeight,
+            "width_css":  picture.thumbWidth+2,
+            "height_css": picture.thumbHeight+2
         })
 
     echo "Generate small thumbnails!"

@@ -220,7 +340,7 @@ proc generateWebsite(targetDir: string, album: Album, config: JsonNode) =
     echo "Generate album page!"
     writeFile(joinPath(targetDir, "index.html"), render(asset_album_html, templateContext))
 
-    removeOrphans(targetDir, config)
+    removeOrphans(targetDir)
     echo "\n"
 
 proc main = 

@@ -239,22 +359,20 @@ proc main =
 
         if readLine(stdin) == "y":
             var config = newConfig()
-            config.setSectionKey("", "SourceDir", "./foobar")
-            config.setSectionKey("", "TargetDir", "./out")
+            config.setSectionKey("",            "SourceDir",              "./foobar")
+            config.setSectionKey("",            "TargetDir",              "./out")
 
-            config.setSectionKey("Site",        "Author",                 "ctucx")
-            config.setSectionKey("Site",        "Name",                   "ctucx' bahnbilder")
+            config.setSectionKey("Site",        "Author",                 "Max Musermann")
+            config.setSectionKey("Site",        "Name",                   "ctucx gallery sample")
             config.setSectionKey("Site",        "Description",            "a short discription for your site")
             config.setSectionKey("Site",        "Tags",                   "a list of tags for seo stuff")
             config.setSectionKey("Site",        "ShowOriginalsButton",    "true")
             config.setSectionKey("Site",        "SymlinkOriginals",       "false")
             config.setSectionKey("Site",        "EnableJS",               "true")
 
-            config.setSectionKey("Thumbnails",  "MediumMaxWidth",         "1920")
-            config.setSectionKey("Thumbnails",  "MediumMaxHeight",        "1080")
-            config.setSectionKey("Thumbnails",  "ThumbMaxWidth",          "200")
-            config.setSectionKey("Thumbnails",  "ThumbMaxHeight",         "200")
-            config.setSectionKey("Thumbnails",  "ThumbQuality",           "90")
+            config.setSectionKey("Thumbnails",  "SmallWidth",             "200")
+            config.setSectionKey("Thumbnails",  "SmallHeight",            "200")
+            config.setSectionKey("Thumbnails",  "SmallQuality",           "90")
 
             config.writeConfig(paramStr(1))
             echo "Have written a default config to this file: " & paramStr(1)

@@ -263,35 +381,41 @@ proc main =
         else:
             quit()            
 
-    var config = %* {}
-
     try:
         let configFile = loadConfig(paramStr(1))
-        config     = %* {
-            "SourceDir":            normalizedPath(configFile.getSectionValue("", "SourceDir",           "")),
-            "TargetDir":            normalizedPath(configFile.getSectionValue("", "TargetDir",           "")),
-            "SiteName":             configFile.getSectionValue("Site",            "Name",                "default title - change me plese"),
-            "SiteAuthor":           configFile.getSectionValue("Site",            "Author",              "Max Mustermann"),
-            "SiteTags":             configFile.getSectionValue("Site",            "Tags",                ""),
-            "SiteDescription":      configFile.getSectionValue("Site",            "Description",         ""),
-            "SiteShowOrigBtn":      configFile.getSectionValue("Site",            "ShowOriginalsButton", "true").parseBool,
-            "SiteSymlinkOrig":      configFile.getSectionValue("Site",            "SymlinkOriginals",    "true").parseBool,
-            "SiteEnableJS":         configFile.getSectionValue("Site",            "EnableJS",            "true").parseBool,
-            "ThumbMediumMaxWidth":  configFile.getSectionValue("Thumbnails",      "MediumMaxWidth",      "1920").parseInt,
-            "ThumbMediumMaxHeight": configFile.getSectionValue("Thumbnails",      "MediumMaxHeight",     "1080").parseInt,
-            "ThumbThumbMaxWidth":   configFile.getSectionValue("Thumbnails",      "ThumbMaxWidth",       "200").parseInt,
-            "ThumbThumbMaxHeight":  configFile.getSectionValue("Thumbnails",      "ThumbMaxHeight",      "200").parseInt,
-            "ThumbThumbQuality":    configFile.getSectionValue("Thumbnails",      "ThumbQuality",        "90").parseInt
-        }
-
-        if config["SourceDir"].getStr == "":
+        config = Config(
+            sourceDir:           normalizedPath(configFile.getSectionValue("", "SourceDir",           "")),
+            targetDir:           normalizedPath(configFile.getSectionValue("", "TargetDir",           "")),
+            siteName:            configFile.getSectionValue("Site",            "Name",                "default title - change me plese"),
+            siteAuthor:          configFile.getSectionValue("Site",            "Author",              "Max Mustermann"),
+            siteTags:            configFile.getSectionValue("Site",            "Tags",                ""),
+            siteDescription:     configFile.getSectionValue("Site",            "Description",         ""),
+            showOriginalsButton: configFile.getSectionValue("Site",            "ShowOriginalsButton", "true").parseBool,
+            symlinkOriginals:    configFile.getSectionValue("Site",            "SymlinkOriginals",    "true").parseBool,
+            enableJS:            configFile.getSectionValue("Site",            "EnableJS",            "true").parseBool,
+            thumbMediumWidth:    configFile.getSectionValue("Thumbnails",      "MediumWidth",         "0").parseInt,
+            thumbMediumHeight:   configFile.getSectionValue("Thumbnails",      "MediumHeight",        "0").parseInt,
+            thumbSmallWidth:     configFile.getSectionValue("Thumbnails",      "SmallWidth",          "0").parseInt,
+            thumbSmallHeight:    configFile.getSectionValue("Thumbnails",      "SmallHeight",         "0").parseInt,
+            thumbSmallQuality:   configFile.getSectionValue("Thumbnails",      "SmallQuality",        "90").parseInt
+        )
+
+        if config.sourceDir == "":
             echo "Config-value 'SourceDir' has to be set!"
             quit(QuitFailure)
 
-        if config["TargetDir"].getStr == "":
+        if config.targetDir == "":
             echo "Config-value 'TargetDir' has to be set!"
             quit(QuitFailure)
 
+        if config.thumbMediumWidth == 0 and config.thumbMediumHeight == 0:
+            config.thumbMediumWidth  = 1920
+            config.thumbMediumHeight = 1080
+
+        if config.thumbSmallWidth == 0 and config.thumbSmallHeight == 0:
+            config.thumbSmallWidth  = 200
+            config.thumbSmallHeight = 200
+
     except ValueError:
         let
          e = getCurrentException()

@@ -304,23 +428,23 @@ proc main =
         echo "Unknown exception while parsing of configuration!"
         quit(QuitFailure)
 
-    if not dirExists(config["SourceDir"].getStr):
+    if not dirExists(config.sourceDir):
         echo "The source directory does not exist!\nBye!"
         quit(QuitFailure)
 
-    if not dirExists(config["TargetDir"].getStr):
+    if not dirExists(config.targetDir):
         echo "The target directory does not exist!\nBye!"
         quit(QuitFailure)
 
   
-    let mainAlbum = createAlbum(config["SourceDir"].getStr, true)
+    let mainAlbum = createAlbum(config.sourceDir, true)
 
-    if config["SiteSymlinkOrig"].getBool != false:
-        if not symlinkExists(joinPath(config["TargetDir"].getStr, "originals")):
-            createSymlink(config["SourceDir"].getStr, joinPath(config["TargetDir"].getStr, "originals"))
+    if config.symlinkOriginals != false:
+        if not symlinkExists(joinPath(config.targetDir, "originals")):
+            createSymlink(config.sourceDir, joinPath(config.targetDir, "originals"))
  
-    placeAssets(config["TargetDir"].getStr, config["SiteEnableJS"].getBool)
-    generateWebsite(config["TargetDir"].getStr, mainAlbum, config)
+    placeAssets(config.targetDir, config.enableJS)
+    generateWebsite(config.targetDir, mainAlbum)
 
 
 main()