This commit is contained in:
Minecon724 2025-01-10 18:50:12 +01:00
parent e6cd257238
commit e3e07dc3fe
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
8 changed files with 273 additions and 84 deletions

View file

@ -32,9 +32,9 @@
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20250107</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>

View file

@ -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<String, Object> 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<String> getArgumentNames() {
return List.of("blur");
}
}
*/

View file

@ -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 {

View file

@ -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");

View file

@ -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<String, Function> getFunctions() {
return Map.of(
"static", new Function() {
@Override
public List<String> getArgumentNames() {
return List.of("path");
}
@Override
public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
return "/static/" + args.get("path"); // TODO for more advanced stuff
}
},
"asset", new Function() {
@Override
public List<String> getArgumentNames() {
return List.of("path");
}
@Override
public Object execute(Map<String, Object> 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<Post> posts) throws IOException {
Map<String, Object> 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<String, Object> context = Map.of(
"site", site,
"article", post
);
var writer = new StringWriter();
articleTemplate.evaluate(writer, context);
return MinifyHtml.minify(writer.toString(), configuration);
}
}

View file

@ -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<Post> posts) {
var content = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><rss version=\"2.0\">";
content += "<channel><title>%s</title><link>%s</link>".formatted(site.name(), site.baseUrl());
var formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz");
for (var post : posts) {
content += "<item><title>%s</title><link>%s/post/%s.html</link><description>%s</description><pubDate>%s</pubDate></item>".formatted(post.title(), site.baseUrl(), post.slug(), post.summary(), post.createdAt().format(formatter));
}
content += "</channel></rss>";
return content;
}
}

View file

@ -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<String, Object>();
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<String, Object> 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;
}
}*/
}

View file

@ -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<Post> posts) throws IOException {
Map<String, Object> 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<String, Object> context = Map.of(
"site", site,
"article", post
);
var writer = new StringWriter();
articleTemplate.evaluate(writer, context);
return MinifyHtml.minify(writer.toString(), configuration);
}
}