diff --git a/pom.xml b/pom.xml
index fe2ce88..ebbbea8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,9 +32,9 @@
3.2.2
- com.google.code.gson
- gson
- 2.11.0
+ org.json
+ json
+ 20250107
commons-io
diff --git a/src/main/java/eu/m724/blog/ImageFilter.java b/src/main/java/eu/m724/blog/ImageFilter.java
new file mode 100644
index 0000000..cf5dfb9
--- /dev/null
+++ b/src/main/java/eu/m724/blog/ImageFilter.java
@@ -0,0 +1,88 @@
+/*package eu.m724.blog;
+
+import io.pebbletemplates.pebble.error.PebbleException;
+import io.pebbletemplates.pebble.extension.Filter;
+import io.pebbletemplates.pebble.template.EvaluationContext;
+import io.pebbletemplates.pebble.template.PebbleTemplate;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.ConvolveOp;
+import java.awt.image.Kernel;
+import java.io.IOException;
+import java.nio.Buffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class ImageFilter implements Filter {
+ private final Path directory;
+
+ public ImageFilter(Path directory) {
+ this.directory = directory;
+ }
+
+ @Override
+ public Object apply(Object input, Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) throws PebbleException {
+ Path path = null;
+ System.out.println("filter");
+
+ if (input instanceof Path p) {
+ path = directory.resolve(p);
+ } else if (input instanceof String s) {
+ path = Path.of(directory.toString(), s);
+ }
+
+ System.out.println(path);
+
+ if (path == null || !Files.isRegularFile(path))
+ return null;
+
+ var filter = "fx-";
+ var out = path.resolveSibling("f").resolve(filter + path.getFileName());
+
+ if (Files.isRegularFile(out)) {
+ return directory.relativize(out).toString();
+ }
+
+ System.out.println(path);
+ BufferedImage image;
+ try (var is = Files.newInputStream(path)) {
+ image = ImageIO.read(is);
+ } catch (IOException e) {
+ System.err.println("Error processing " + path + " with " + filter + ": " + e.getMessage());
+ return null;
+ }
+
+ var blur = ((Long) args.getOrDefault("blur", 0)).intValue();
+
+ if (blur > 0) {
+ int size = blur * 2 + 1;
+ float[] data = new float[size * size];
+
+ Arrays.fill(data, 1.0f / (size * size));
+
+ Kernel kernel = new Kernel(size, size, data);
+ ConvolveOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_ZERO_FILL, null);
+ image = op.filter(image, null);
+ }
+
+ try (var os = Files.newOutputStream(out)) {
+ ImageIO.write(image, "webp", os);
+ } catch (IOException e) {
+ System.err.println("Error processing " + path + " with " + filter + ": " + e.getMessage());
+ return null;
+ }
+
+ return directory.relativize(out).toString();
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("blur");
+ }
+}
+*/
\ No newline at end of file
diff --git a/src/main/java/eu/m724/blog/Main.java b/src/main/java/eu/m724/blog/Main.java
index 8a450b6..09b5884 100644
--- a/src/main/java/eu/m724/blog/Main.java
+++ b/src/main/java/eu/m724/blog/Main.java
@@ -1,8 +1,8 @@
package eu.m724.blog;
+import eu.m724.blog.data.Feed;
import eu.m724.blog.data.Post;
import eu.m724.blog.data.Site;
-import eu.m724.blog.data.Template;
import in.wilsonl.minifyhtml.Configuration;
import in.wilsonl.minifyhtml.MinifyHtml;
import org.apache.commons.io.file.PathUtils;
@@ -27,11 +27,16 @@ public class Main {
public static void main(String[] args) throws IOException {
System.out.println("Hello world!");
+ var start = System.nanoTime();
+
var workingDirectory = Path.of("m724");
var templateDirectory = workingDirectory.resolve("template");
var outputDirectory = workingDirectory.resolve("generated_out");
var force = true;
+ var server = true;
+ var openBrowser = true;
+
//
var repository = new RepositoryBuilder()
@@ -50,7 +55,7 @@ public class Main {
}
var site = Site.fromConfig(git);
- var template = new Template(templateDirectory);
+ var template = new TemplateRenderer(outputDirectory, templateDirectory);
Files.createDirectory(outputDirectory);
@@ -88,7 +93,27 @@ public class Main {
posts.sort(Comparator.comparing(Post::createdAt).reversed());
Files.writeString(outputDirectory.resolve("index.html"), template.renderIndex(site, posts));
- new Server(outputDirectory).start();
+ Files.writeString(outputDirectory.resolve("posts.rss"), Feed.generateRss(site, posts));
+
+
+ var end = System.nanoTime();
+ System.out.printf("Exported to %s (%.2f ms)\n", outputDirectory, (end - start) / 1000000.0);
+
+ if (server) {
+ var listenAddress = new Server(outputDirectory).start();
+ if (openBrowser) {
+ try {
+ var process = Runtime.getRuntime().exec(new String[] { "xdg-open", "http://" + listenAddress.getHostString() + ":" + listenAddress.getPort() });
+ var code = process.waitFor();
+ if (code != 0) {
+ throw new Exception("Exit code " + code);
+ }
+ System.out.println("Opened browser");
+ } catch (Exception e) {
+ System.out.println("Failed to open browser: " + e);
+ }
+ }
+ }
}
private static boolean copyTree(Path srcDir, Path destDir) throws IOException {
diff --git a/src/main/java/eu/m724/blog/Server.java b/src/main/java/eu/m724/blog/Server.java
index 29bf223..27e6bce 100644
--- a/src/main/java/eu/m724/blog/Server.java
+++ b/src/main/java/eu/m724/blog/Server.java
@@ -11,23 +11,31 @@ import java.nio.file.Files;
import java.nio.file.Path;
public class Server implements HttpHandler {
- private final InetSocketAddress listenAddress = new InetSocketAddress("localhost",8010);
- private final Path directory;
+ private final InetSocketAddress listenAddress;
+ private final Path webroot;
- public Server(Path directory) {
- this.directory = directory;
+ public Server(InetSocketAddress listenAddress, Path webroot) {
+ this.listenAddress = listenAddress;
+ this.webroot = webroot;
}
- public void start() throws IOException {
+ public Server(Path webroot) {
+ this(new InetSocketAddress("localhost", 0), webroot);
+ }
+
+ public InetSocketAddress start() throws IOException {
var server = HttpServer.create(listenAddress, 0);
server.createContext("/", this);
server.start();
- System.out.println("Server started on " + listenAddress);
+
+ System.out.println("Server started on " + server.getAddress());
+
+ return server.getAddress();
}
@Override
public void handle(HttpExchange exchange) throws IOException {
- var path = directory.resolve(exchange.getRequestURI().getPath().substring(1));
+ var path = webroot.resolve(exchange.getRequestURI().getPath().substring(1));
if (Files.isDirectory(path)) {
path = path.resolve("index.html");
diff --git a/src/main/java/eu/m724/blog/TemplateRenderer.java b/src/main/java/eu/m724/blog/TemplateRenderer.java
new file mode 100644
index 0000000..d0af295
--- /dev/null
+++ b/src/main/java/eu/m724/blog/TemplateRenderer.java
@@ -0,0 +1,95 @@
+package eu.m724.blog;
+
+import eu.m724.blog.data.Post;
+import eu.m724.blog.data.Site;
+import in.wilsonl.minifyhtml.Configuration;
+import in.wilsonl.minifyhtml.MinifyHtml;
+import io.pebbletemplates.pebble.PebbleEngine;
+import io.pebbletemplates.pebble.extension.AbstractExtension;
+import io.pebbletemplates.pebble.extension.Function;
+import io.pebbletemplates.pebble.loader.FileLoader;
+import io.pebbletemplates.pebble.template.EvaluationContext;
+import io.pebbletemplates.pebble.template.PebbleTemplate;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class TemplateRenderer {
+ private final Configuration configuration;
+ private final PebbleTemplate indexTemplate, articleTemplate;
+
+ public TemplateRenderer(Path outputDirectory, Path templateDirectory) {
+ this.configuration = new Configuration.Builder()
+ .setMinifyCss(true)
+ .setMinifyJs(true)
+ .build();
+
+ var loader = new FileLoader();
+ loader.setPrefix(templateDirectory.toString());
+ loader.setSuffix(".html");
+
+ var pebbleEngine = new PebbleEngine.Builder()
+ .loader(loader)
+ .extension(new AbstractExtension() {
+ @Override
+ public Map getFunctions() {
+ return Map.of(
+ "static", new Function() {
+ @Override
+ public List getArgumentNames() {
+ return List.of("path");
+ }
+
+ @Override
+ public Object execute(Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ return "/static/" + args.get("path"); // TODO for more advanced stuff
+ }
+ },
+ "asset", new Function() {
+ @Override
+ public List getArgumentNames() {
+ return List.of("path");
+ }
+
+ @Override
+ public Object execute(Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ return "/assets/" + args.get("path"); // TODO for more advanced stuff
+ }
+ }
+ );
+ }
+ })
+ .build();
+
+ this.indexTemplate = pebbleEngine.getTemplate("index_template");
+ this.articleTemplate = pebbleEngine.getTemplate("article_template");
+ }
+
+ public String renderIndex(Site site, ArrayList posts) throws IOException {
+ Map context = Map.of(
+ "site", site,
+ "articles", posts
+ );
+
+ var writer = new StringWriter();
+ indexTemplate.evaluate(writer, context);
+
+ return MinifyHtml.minify(writer.toString(), configuration);
+ }
+
+ public String renderPost(Site site, Post post) throws IOException {
+ Map context = Map.of(
+ "site", site,
+ "article", post
+ );
+
+ var writer = new StringWriter();
+ articleTemplate.evaluate(writer, context);
+
+ return MinifyHtml.minify(writer.toString(), configuration);
+ }
+}
diff --git a/src/main/java/eu/m724/blog/data/Feed.java b/src/main/java/eu/m724/blog/data/Feed.java
new file mode 100644
index 0000000..82c9b03
--- /dev/null
+++ b/src/main/java/eu/m724/blog/data/Feed.java
@@ -0,0 +1,20 @@
+package eu.m724.blog.data;
+
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+public class Feed {
+ public static String generateRss(Site site, List posts) {
+ var content = "";
+ content += "%s%s".formatted(site.name(), site.baseUrl());
+
+ var formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz");
+ for (var post : posts) {
+ content += "- %s%s/post/%s.html%s%s
".formatted(post.title(), site.baseUrl(), post.slug(), post.summary(), post.createdAt().format(formatter));
+ }
+
+ content += "";
+
+ return content;
+ }
+}
diff --git a/src/main/java/eu/m724/blog/data/Site.java b/src/main/java/eu/m724/blog/data/Site.java
index 06e2508..68721a6 100644
--- a/src/main/java/eu/m724/blog/data/Site.java
+++ b/src/main/java/eu/m724/blog/data/Site.java
@@ -1,16 +1,12 @@
package eu.m724.blog.data;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
import org.eclipse.jgit.api.Git;
+import org.json.JSONObject;
import java.io.IOException;
import java.nio.file.Files;
-import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
-import java.util.stream.Collectors;
public record Site(
String name,
@@ -20,22 +16,24 @@ public record Site(
) {
public static Site fromConfig(Git git) throws IOException {
var content = Files.readString(git.getRepository().getDirectory().toPath().getParent().resolve("config.json"));
- var json = JsonParser.parseString(content).getAsJsonObject().asMap();
+ var json = new JSONObject(content);
String name = null;
String baseUrl = null;
var custom = new HashMap();
- for (var entry : json.entrySet()) {
- switch (entry.getKey()) {
+ for (var key : json.keySet()) {
+ var value = json.get(key);
+
+ switch (key) {
case "name":
- name = entry.getValue().getAsString();
+ name = (String) value;
break;
case "baseUrl":
- baseUrl = entry.getValue().getAsString();
+ baseUrl = (String) value;
break;
default:
- custom.put(entry.getKey(), eToO(entry.getValue()));
+ custom.put(key, value);
}
}
@@ -44,6 +42,8 @@ public record Site(
);
}
+ /* remained from gson
+
private static Map deep(JsonObject jsonObject) {
return jsonObject.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), eToO(e.getValue())))
@@ -56,9 +56,21 @@ public record Site(
} else if (element.isJsonObject()) {
return deep(element.getAsJsonObject());
} else if (element.isJsonPrimitive()) {
+ try {
+ return element.getAsBoolean();
+ } catch (IllegalStateException e) { }
+
+ try {
+ return element.getAsLong();
+ } catch (NumberFormatException e) { }
+
+ try {
+ return element.getAsDouble();
+ } catch (NumberFormatException e) { }
+
// TODO
}
return null;
- }
+ }*/
}
diff --git a/src/main/java/eu/m724/blog/data/Template.java b/src/main/java/eu/m724/blog/data/Template.java
deleted file mode 100644
index 06fef1d..0000000
--- a/src/main/java/eu/m724/blog/data/Template.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package eu.m724.blog.data;
-
-import in.wilsonl.minifyhtml.Configuration;
-import in.wilsonl.minifyhtml.MinifyHtml;
-import io.pebbletemplates.pebble.PebbleEngine;
-import io.pebbletemplates.pebble.loader.FileLoader;
-import io.pebbletemplates.pebble.template.PebbleTemplate;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.nio.file.Path;
-import java.util.*;
-
-public class Template {
- private final Configuration configuration;
- private final PebbleTemplate indexTemplate, articleTemplate;
-
- public Template(Path directory) {
- this.configuration = new Configuration.Builder()
- .setMinifyCss(true)
- .setMinifyJs(true)
- .build();
-
- var loader = new FileLoader();
- loader.setPrefix(directory.toString());
- loader.setSuffix(".html");
-
- var pebbleEngine = new PebbleEngine.Builder()
- .loader(loader)
- .build();
-
- this.indexTemplate = pebbleEngine.getTemplate("index_template");
- this.articleTemplate = pebbleEngine.getTemplate("article_template");
- }
-
- public String renderIndex(Site site, ArrayList posts) throws IOException {
- Map context = Map.of(
- "site", site,
- "articles", posts
- );
-
- var writer = new StringWriter();
- indexTemplate.evaluate(writer, context);
-
- return MinifyHtml.minify(writer.toString(), configuration);
- }
-
- public String renderPost(Site site, Post post) throws IOException {
- Map context = Map.of(
- "site", site,
- "article", post
- );
-
- var writer = new StringWriter();
- articleTemplate.evaluate(writer, context);
-
- return MinifyHtml.minify(writer.toString(), configuration);
- }
-}