eleventy.config.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. module.exports = function(eleventyConfig) {
  2. const fs = require('fs');
  3. const path = require('path');
  4. const exifParser = require("exif-parser");
  5. // add my css file from the root folder
  6. eleventyConfig.addPassthroughCopy("bundle.css");
  7. eleventyConfig.addPassthroughCopy("img");
  8. eleventyConfig.addPassthroughCopy({ "_data/images": "photos" });
  9. // This line was to make sure my post.md weren't processed as Liquid,
  10. // permitting this post here
  11. eleventyConfig.setTemplateFormats(["md", "njk", "html"]);
  12. eleventyConfig.addGlobalData("site.author", "unakt");
  13. // Double with the .eleventyignore file, but just in case
  14. eleventyConfig.ignores.add("/_drafts/**");
  15. eleventyConfig.ignores.add("/README.md");
  16. eleventyConfig.addLayoutAlias("post", "post.njk");
  17. eleventyConfig.addCollection("postlist", function(collectionApi) {
  18. return collectionApi.getFilteredByGlob("./posts/*.md");
  19. });
  20. // Custom collection for mini images
  21. // It scans the ./src/_data/images/ folder for jpg files
  22. // and groups them by their parent folder name
  23. // Each image object contains its path, album name, and last modified date
  24. // ========================================================
  25. eleventyConfig.addCollection("miniImages", function(collectionApi) {
  26. const files = [];
  27. const baseDir = "./_data/images/";
  28. // Recursive function to find all images
  29. const getFiles = (dir) => {
  30. if (!fs.existsSync(dir)) return; // Safety check
  31. fs.readdirSync(dir).forEach(file => {
  32. const fullPath = path.join(dir, file);
  33. if (fs.statSync(fullPath).isDirectory()) {
  34. getFiles(fullPath);
  35. } else if (file.endsWith(".jpg") ||
  36. file.endsWith(".jpeg") ||
  37. file.endsWith(".png") ||
  38. file.endsWith(".gif") ||
  39. file.endsWith(".webp")) {
  40. // .jpg an others extensions might be case sensitive on some systems !!
  41. // READ EXIF DATA
  42. let exifDate = null;
  43. try {
  44. const buffer = fs.readFileSync(fullPath);
  45. const parser = exifParser.create(buffer);
  46. const result = parser.parse();
  47. // Grab Exif Date of photo taken (if exists)
  48. if (result.tags.DateTimeOriginal) {
  49. exifDate = new Date(result.tags.DateTimeOriginal * 1000);
  50. }
  51. exifData = {
  52. model: result.tags.Model || "Unknown",
  53. lens: result.tags.LensModel || "Unknown",
  54. focalLength: result.tags.FocalLength ? `${result.tags.FocalLength}` : "N/A",
  55. fNumber: result.tags.FNumber ? `f/${result.tags.FNumber}` : "N/A",
  56. exposure: result.tags.ExposureTime ? `1/${Math.round(1/result.tags.ExposureTime)}s` : "N/A",
  57. iso: result.tags.ISO || "N/A"
  58. };
  59. } catch (e) {
  60. console.error(`Skipping ${file}: No valid EXIF date found.`);
  61. }
  62. // END READ EXIF DATA
  63. // BEFORE =============
  64. const relativePath = path.relative("./_data/images", fullPath).replace(/\\/g, "/");
  65. //const pathParts = relativePath.split(path.sep).slice(0, -1); // Make Breadcrumb
  66. // ====================
  67. // Only add real photos with EXIF date
  68. if (exifDate) {
  69. files.push({
  70. fullPath: fullPath, // We need this for the calculation below
  71. //webPath: fullPath.replace("src/", "/"),
  72. date: exifDate,
  73. path: "/photos/" + relativePath,
  74. folderPath: path.relative(baseDir, path.dirname(fullPath)),
  75. exif: exifData,
  76. });
  77. }
  78. }
  79. });
  80. };
  81. getFiles(baseDir);
  82. // Group them by album name
  83. const grouped = {};
  84. // This here ? or just before return grouped ?
  85. files.sort((a, b) => a.date - b.date);
  86. files.forEach(f => {
  87. const relFolder = path.relative(baseDir, path.dirname(f.fullPath));
  88. if (!grouped[relFolder]) {
  89. grouped[relFolder] = {
  90. images: [],
  91. subfolders: [],
  92. breadcrumbArray: relFolder === "" ? [] : relFolder.split(path.sep)
  93. //path: folderPath.split(path.sep) // Normalize to forward slashes
  94. };
  95. }
  96. grouped[relFolder].images.push(f);
  97. });
  98. // Second pass to link parents to children subfolders
  99. for (const parentPath in grouped) {
  100. for (const potentialChild in grouped) {
  101. const parentParts = parentPath === "" ? [] : parentPath.split(path.sep);
  102. const childParts = potentialChild.split(path.sep);
  103. // Is the child exactly one level deeper than the parent?
  104. if (potentialChild.startsWith(parentPath) &&
  105. childParts.length === parentParts.length + 1 &&
  106. parentPath !== potentialChild) {
  107. grouped[parentPath].subfolders.push(potentialChild);
  108. }
  109. }
  110. }
  111. return grouped;
  112. });
  113. // ========================================================
  114. eleventyConfig.addCollection("allPhotos", function(collectionApi) {
  115. //const grouped = collectionApi.getFilteredByTag("miniImages"); // This won't work since miniImages isn't a tag
  116. // Instead, use the logic we already built:
  117. const miniImages = collectionApi.getAll()[0].data.collections.miniImages;
  118. return Object.values(miniImages).flatMap(album => album.images);
  119. });
  120. // Date filter to format dates as YYYY-MM-DD
  121. //eleventyConfig.addFilter("date", function(dateObj, format = "YYYY-MM-DD") {
  122. // const d = new Date(dateObj);
  123. // return d.toISOString().split('T')[0]; // Returns YYYY-MM-DD
  124. // });
  125. //eleventyConfig.addFilter("date", function(dateObj) {
  126. // const d = new Date(dateObj);
  127. //
  128. // Check if the date is actually valid
  129. // if (isNaN(d.getTime())) {
  130. // console.warn("Skipping invalid date for a file. Value received:", dateObj);
  131. // return "Date Unknown";
  132. // }
  133. // return d.toISOString().split('T')[0];
  134. //});
  135. return {
  136. // Ok idk this prevent markdown from being processed?
  137. markdownTemplateEngine: false,
  138. dir: {
  139. input: "./",
  140. output: "_site",
  141. layouts: "layouts",
  142. includes: "includes"
  143. }
  144. };
  145. };