ctucx.git: gallery

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

commit 7dec2aefa1f2ffc7a10443d33494886d06d3e000
parent 3a72255b1ceb521fa5e3cbf98188c81a7a0a7797
Author: ctucx <leah@antifa.jetzt>
Date: Tue, 16 Jun 2020 23:19:15 +0200

general code-refactoring
3 files changed, 79 insertions(+), 32 deletions(-)
M
src/assets/album.html
|
4
++--
M
src/assets/picture.html
|
33
++++++++++++++++++++++++---------
M
src/gallery.nim
|
74
+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
diff --git a/src/assets/album.html b/src/assets/album.html
@@ -114,8 +114,8 @@
 			{{/hasSubalbums}}
 
 			{{#pictures}}
-			<a href="{{name}}/" class="photo">
-				<img src="./thumbnails/{{name}}.png" alt="Photo thumbnail" width="{{ThumbThumbMaxWidth}}" height="{{ThumbThumbMaxHeight}}">
+			<a href="{{name}}.html" class="photo">
+				<img src="thumbnails/{{name}}.png" alt="Photo thumbnail" width="{{ThumbThumbMaxWidth}}" height="{{ThumbThumbMaxHeight}}">
 				<span class="overlay">
 					<h1>{{name}}</h1>
 					<!--<p><span title="Camera Date"><svg class="iconic "><use xlink:href="/iconic.svg#camera-slr"></use></svg></span></p>-->
diff --git a/src/assets/picture.html b/src/assets/picture.html
@@ -13,18 +13,18 @@
 		<meta name="apple-mobile-web-app-status-bar-style" content="black">
 		<meta name="apple-mobile-web-app-capable" content="yes">
 
-		<meta property="og:image" content="../medium/{{name}}.jpg" />
+		<meta property="og:image" content="medium/{{filename}}" />
 
 		<link type="text/css" rel="stylesheet" href="/style.css">
 		<link rel="shortcut icon" href="favicon.ico">
 	</head>
 	<body>
 		<header class="header header-view" style="display: flex;">
-			<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="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}}
-			<a class="button" href="/originals{{orig}}" title="Download"><svg class="iconic"><use xlink:href="/iconic.svg#cloud-download"></use></svg></a>
+			<a class="button" href="{{orig}}" title="Download"><svg class="iconic"><use xlink:href="/iconic.svg#cloud-download"></use></svg></a>
 			{{/SiteShowOrigBtn}}
 			
 			<a class="header-divider"></a>

@@ -140,11 +140,11 @@
 		</header>
 
 		<div id="imageview" class="fadeIn full" style="display: block;">
-			<img id="image" class="" src="../medium/{{name}}.jpg">
+			<img id="image" class="" src="medium/{{filename}}">
 			
 			{{#hasPrev}}
 			<div class="arrow_wrapper arrow_wrapper--previous">
-				<a id="previous" href="../{{prev_name}}" style="background-image: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), url(&quot;../thumbnails/{{prev_name}}.png&quot;);">
+				<a id="previous" href="{{prev_name}}.html" style="background-image: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), url(&quot;thumbnails/{{prev_name}}.png&quot;);">
 					<svg class="iconic "><use xlink:href="/iconic.svg#caret-left"></use></svg>
 				</a>
 			</div>

@@ -152,7 +152,7 @@
 
 			{{#hasNext}}
 			<div class="arrow_wrapper arrow_wrapper--next">
-				<a id="next" href="../{{next_name}}" style="background-image: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), url(&quot;../thumbnails/{{next_name}}.png&quot;);">
+				<a id="next" href="{{next_name}}.html" style="background-image: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), url(&quot;thumbnails/{{next_name}}.png&quot;);">
 					<svg class="iconic "><use xlink:href="/iconic.svg#caret-right"></use></svg>
 				</a>
 			</div>

@@ -163,9 +163,24 @@
 		<script type="text/javascript">
 			window.onload=getExif;
 			window.onkeyup = function(e) {
-				if (e.keyCode == 27) window.location = "..";
-				if (e.keyCode == 39) window.location = "../{{next_name}}";
-				if (e.keyCode == 37) window.location = "../{{prev_name}}";
+				if (e.keyCode == 27) window.location = "./";
+				
+				{{#hasNext}}
+				if (e.keyCode == 39) window.location = "./{{next_name}}.html";
+				{{/hasNext}}
+
+				{{^hasNext}}
+				if (e.keyCode == 39) window.location = "./";
+				{{/hasNext}}
+
+				{{#hasPrev}}
+				if (e.keyCode == 37) window.location = "./{{prev_name}}.html";
+				{{/hasPrev}}
+
+				{{^hasPrev}}
+				if (e.keyCode == 37) window.location = "./";
+				{{/hasPrev}}
+				
 				if (e.keyCode == 32) document.getElementById("toggle").checked = true;
 			}
 
diff --git a/src/gallery.nim b/src/gallery.nim
@@ -1,4 +1,4 @@
-import os, osproc, options, json, strutils, sequtils, random, algorithm, parsecfg, tables
+import os, osproc, options, json, strutils, random, algorithm, parsecfg
 import moustachu
 
 const asset_exif_js      = staticRead"./assets/exif.js"

@@ -21,6 +21,7 @@ type
         name*:     string
         path*:     string
         filename*: string
+        filetype*: string
         desc*:     Option[string]
         size*:     BiggestInt
 

@@ -61,6 +62,7 @@ proc createPicture(path: string): Picture =
     result.name       = name
     result.path       = dir
     result.filename   = lastPathPart(path)
+    result.filetype   = ext.replace(".", "")
     result.size       = getFileSize(path)
 
 

@@ -96,6 +98,35 @@ proc placeAssets(targetDir: string, enableJS: bool) =
     if enableJS: writeFile(joinPath(targetDir, "exif.js"), asset_exif_js)
 
 
+proc removeOrphans (sourceDir: string, targetDir: string) = 
+    #Albums
+    for album in walkDir(targetDir):
+        if album.kind != pcDir: continue
+
+        var targetDir = targetDir
+        normalizePath(targetDir)
+
+        var dirname = album.path.replace(targetDir & "/", "")
+        
+        if dirname == "thumbnails": continue
+        if dirname == "medium":     continue 
+
+        #echo dirname
+        #if not dirExists(joinPath(sourceDir, dirname)):
+        #    removeDir(joinPath(targetDir, dirname))
+
+    #Photos
+    for photo in walkDir(joinPath(targetDir, "medium")):
+        let (dir, name, ext) = splitFile(photo.path)
+        let filename         = name & ext
+
+        echo joinPath(sourceDir, filename)
+        #if not fileExists(joinPath(sourceDir, filename)):
+        #    removeFile(joinPath(targetDir, name & ".html"))
+        #    removeFile(joinPath(targetDir, "medium", filename))
+        #    removeFile(joinPath(targetDir, "thumbnails", name & ".png"))
+
+
 proc generateWebsite(sourceDir: string, targetDir: string, album: Album, config: JsonNode) =
     echo "============"
     echo "Create Album:" & album.name

@@ -123,6 +154,7 @@ proc generateWebsite(sourceDir: string, targetDir: string, album: Album, config:
 
     for subalbum in album.subalbums:
         generateWebsite(sourceDir, joinPath(targetDir, subalbum.name), subalbum, config)
+#        removeOrphans(joinPath(sourceDir, subalbum.name), joinPath(targetDir, subalbum.name))
 
         var thumbnail1 = "/no_images.svg"
         var thumbnail2 = "/no_images.svg"

@@ -143,11 +175,10 @@ proc generateWebsite(sourceDir: string, targetDir: string, album: Album, config:
         })
 
     for index, picture in album.pictures:
-        discard existsOrCreateDir(joinPath(targetDir, picture.name))
-
         var pictureTemplateContext = mergeJson(%* {
             "name":        picture.name,
-            "orig":        picture.path.replace(sourceDir, "") & "/" & picture.filename,
+            "orig":        joinPath("/originals", picture.path.replace(sourceDir, ""), picture.filename),
+            "filename":    picture.filename,
             "description": "-",
             "hasPrev":     false,
             "hasNext":     false,

@@ -166,13 +197,13 @@ proc generateWebsite(sourceDir: string, targetDir: string, album: Album, config:
 
 
         echo "Generate picture page: " & picture.name
-        writeFile(joinPath(targetDir, picture.name, "index.html"), render(asset_picture_html, pictureTemplateContext))
+        writeFile(joinPath(targetDir, picture.name & ".html"), render(asset_picture_html, pictureTemplateContext))
 
         if not fileExists(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 not fileExists(targetDir & "/medium/" & picture.name & ".jpg"):
-            mediumThumbnails.add("/usr/bin/env mogrify -format jpg -path " & quoteShell(joinPath(targetDir, "medium")) & " -resize " & $config["ThumbMediumMaxWidth"].getInt & "x\\> " & quoteShell(joinPath(picture.path, picture.filename)))
+            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,

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

@@ -210,13 +242,13 @@ proc main =
             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",  "EnableJS",               "true")
 
             config.writeConfig(paramStr(1))
             echo "Have written a default config to this file: " & paramStr(1)

@@ -228,20 +260,20 @@ proc main =
     try:
         var configFile = loadConfig(paramStr(1))
         config     = %* {
-            "SourceDir":             configFile.getSectionValue("",          "SourceDir"),
-            "TargetDir":             configFile.getSectionValue("",          "TargetDir"),
-            "SiteName":              configFile.getSectionValue("Site",      "Name"),
-            "SiteAuthor":            configFile.getSectionValue("Site",      "Author"),
-            "SiteDescription":       configFile.getSectionValue("Site",      "Description"),
-            "SiteTags":              configFile.getSectionValue("Site",      "Tags"),
-            "SiteShowOrigBtn":       configFile.getSectionValue("Site",      "ShowOriginalsButton").parseBool,     #not implemented yet
-            "SiteSymlinkOrig":       configFile.getSectionValue("Site",      "SymlinkOriginals").parseBool,        #not implemented yet
-            "SiteEnableJS":          configFile.getSectionValue("Site",      "EnableJS").parseBool,
-            "ThumbMediumMaxWidth":   configFile.getSectionValue("Thumbnail", "MediumMaxWidth").parseInt,          #not implemented yet
-            "ThumbMediumMaxHeight":  configFile.getSectionValue("Thumbnail", "MediumMaxHeight").parseInt,         #not implemented yet
-            "ThumbThumbMaxWidth":    configFile.getSectionValue("Thumbnail", "ThumbMaxWidth").parseInt,           #not implemented yet
-            "ThumbThumbMaxHeight":   configFile.getSectionValue("Thumbnail", "ThumbMaxHeight").parseInt,          #not implemented yet
-            "ThumbThumbQuality":     configFile.getSectionValue("Thumbnail", "ThumbQuality").parseInt             #not implemented yet
+            "SourceDir":             configFile.getSectionValue("",           "SourceDir"),
+            "TargetDir":             configFile.getSectionValue("",           "TargetDir"),
+            "SiteName":              configFile.getSectionValue("Site",       "Name"),
+            "SiteAuthor":            configFile.getSectionValue("Site",       "Author"),
+            "SiteDescription":       configFile.getSectionValue("Site",       "Description"),
+            "SiteTags":              configFile.getSectionValue("Site",       "Tags"),
+            "SiteShowOrigBtn":       configFile.getSectionValue("Site",       "ShowOriginalsButton").parseBool,
+            "SiteSymlinkOrig":       configFile.getSectionValue("Site",       "SymlinkOriginals").parseBool,
+            "SiteEnableJS":          configFile.getSectionValue("Site",       "EnableJS").parseBool,
+            "ThumbMediumMaxWidth":   configFile.getSectionValue("Thumbnails", "MediumMaxWidth").parseInt,
+            "ThumbMediumMaxHeight":  configFile.getSectionValue("Thumbnails", "MediumMaxHeight").parseInt,
+            "ThumbThumbMaxWidth":    configFile.getSectionValue("Thumbnails", "ThumbMaxWidth").parseInt,
+            "ThumbThumbMaxHeight":   configFile.getSectionValue("Thumbnails", "ThumbMaxHeight").parseInt,
+            "ThumbThumbQuality":     configFile.getSectionValue("Thumbnails", "ThumbQuality").parseInt
         }
 
     except ValueError: