10 Commits

Author SHA1 Message Date
00b5020642 single tag fix 2025-06-22 20:49:20 +02:00
492ea8755f ... 2025-06-22 20:00:52 +02:00
39da474db6 i am an idiot 2025-06-22 19:33:40 +02:00
44bcd5607f added xapmeta 2025-06-22 18:00:36 +02:00
5ff44a1912 fixed nonexisitng xmp metadata error 2025-06-22 17:53:46 +02:00
4241f3965a added tag redout from xmp subject 2025-06-22 17:50:04 +02:00
002e9c62db added tagging function as requested in issue #7 2025-06-22 16:20:45 +02:00
cf494401c8 removed pbardict 2025-05-30 11:25:32 +02:00
2a0323e579 oop 2025-05-20 09:02:31 +02:00
7e23b3625a fixed logo timeout 2025-03-31 20:24:27 +02:00
52 changed files with 303 additions and 105 deletions

2
.gitignore vendored
View File

@@ -164,7 +164,7 @@ cython_debug/
test/.static
test/.thumbnails
test/**/index.html
test/**/.sizelist.json
test/**/.metadata.json
test/manifest.json
themes/previews
logs

View File

@@ -1 +1 @@
3.12
StaticGalleryBuilder

View File

@@ -1 +1 @@
2.6.1
2.7.1

View File

@@ -97,6 +97,7 @@ To generate a web manifest file:
- The script generates the preview thumbnails in a `.thumbnails` subdirectory within the root folder.
- The `.lock` file prevents multiple instances of the script from running simultaneously. Make sure to remove it if the script terminates unexpectedly.
- Add a `info` file into any directory containing pictures and it will be read and displayed as a tooltip on the website.
- Add tags to the Image xmp `subject` or to `.metadata.json` to tag images for filtering.
## License

View File

@@ -40,6 +40,7 @@
"--reverse-sort",
"--regenerate-thumbnails",
"--reread-metadata",
"--folderthumbnails",
],
"console": "integratedTerminal",
"name": "Testfolder",
@@ -64,6 +65,7 @@
"-m",
// "--regenerate-thumbnails",
// "--reread-metadata",
"--folderthumbnails",
],
"console": "integratedTerminal",
"name": "woek",
@@ -75,7 +77,7 @@
{
"args": [
"${workspaceFolder}/themes",
"https://pictures.sorogon.eu/Analog/Example/"
"https://pictures.sorogon.eu/public/Example/"
],
"console": "integratedTerminal",
"name": "Generate Themes previews",
@@ -99,7 +101,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
},
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.defaultFormatter": "charliermarsh.ruff",
},
"black-formatter.args": [
"-l 260",
@@ -151,6 +153,7 @@
"yaml.schemas": {
"https://raw.githubusercontent.com/pamburus/hl/master/schema/json/config.schema.json": "file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/hl_config.yaml"
},
"ruff.lineLength": 180,
},
"tasks": {
"version": "2.0.0",

View File

@@ -3,6 +3,7 @@ import os
import re
import sys
import shutil
import urllib.error
import urllib.parse
import urllib.request
from multiprocessing import Pool, freeze_support
@@ -33,8 +34,6 @@ IMG_EXTENSIONS = [".jpg", ".jpeg", ".png"]
NOT_LIST = ["*/Galleries/*", "Archives"]
# fmt: on
pbardict: dict[str, tqdm] = {}
args = parse_arguments(VERSION)
lock_file = os.path.join(args.root_directory, ".lock")
@@ -44,7 +43,7 @@ if os.path.exists(lock_file):
else:
from modules.logger import logger
from modules.svg_handling import icons, webmanifest, extract_colorscheme
from modules.generate_html import list_folder, EXCLUDES
from modules.generate_html import list_folder
def init_globals(_args: Args, raw: list[str]) -> tuple[Args, list[str]]:
@@ -174,7 +173,8 @@ def main(args) -> None:
logger.info("getting logo from sorogon.eu")
req = urllib.request.Request("https://files.sorogon.eu/logo.svg")
with urllib.request.urlopen(req) as res:
try:
with urllib.request.urlopen(req, timeout=10) as res:
logo = res.read().decode()
if logo.startswith("<?xml"):
@@ -183,6 +183,8 @@ def main(args) -> None:
logo = re.sub(r"<!--.+-->", "", logo).strip()
logo = logo.replace("\n", " ")
logo = " ".join(logo.split())
except urllib.error.URLError:
logo = "&lt;/srgn&gt;"
if args.reread_metadata:
logger.warning("reread metadata flag is set to true, all image metadata will be reread")

View File

@@ -131,14 +131,18 @@ figure {
text-decoration: none;
}
.navbar .title {
.navbar .navleft {
float: left;
}
.navbar .navcenter {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.navbar .license {
float: right;
.navbar .navright {
float: right
}
.navbar li .header {
@@ -150,18 +154,28 @@ figure {
.tooltip {
position: relative;
cursor: pointer;
}
.tooltip .tooltiptext {
display: none;
cursor: default;
width: max-content;
position: absolute;
z-index: 100;
opacity: 0;
transition: opacity 0.3s;
float: left;
}
.tooltip .infotext {
padding: 12px;
}
.tooltip .tagdropdown {
padding: 0;
}
.tooltip:hover .tooltiptext {
display: block;
opacity: 1;
@@ -172,6 +186,22 @@ figure {
opacity: 1;
}
.tooltip .tooltiptext .tagentry {
list-style: none;
width: 100%;
cursor: pointer;
margin: 0;
padding: 0;
}
.tooltip .tooltiptext .tagentry label {
cursor: pointer;
width: 100%;
height: 100%;
padding: 12px;
display: block;
}
.column {
-ms-flex: 12.5%;
flex: 12.5%;

View File

@@ -1,7 +1,6 @@
import os
import re
import sys
import time
import shutil
import base64
import fileinput

View File

@@ -7,7 +7,7 @@ from typing import Any
from datetime import datetime
from tqdm.auto import tqdm
from PIL import Image, ExifTags, TiffImagePlugin
from PIL import Image, ExifTags, TiffImagePlugin, UnidentifiedImageError
from jinja2 import Environment, FileSystemLoader
from modules.logger import logger
@@ -29,55 +29,60 @@ Image.MAX_IMAGE_PIXELS = 933120000
# Initialize Jinja2 environment for template rendering
env = Environment(loader=FileSystemLoader(os.path.join(SCRIPTDIR, "templates")))
thumbnails: list[tuple[str, str]] = []
thumbnails: list[tuple[str, str, str]] = []
info: dict[str, str] = {}
licens: dict[str, str] = {}
pbardict: dict[str, tqdm] = {}
def initialize_sizelist(folder: str) -> dict[str, dict[str, int]]:
def initialize_metadata(folder: str) -> dict[str, dict[str, int]]:
"""
Initializes the size list JSON file if it doesn't exist.
Initializes the metadata JSON file if it doesn't exist.
Args:
folder (str): The folder in which the size list file is located.
folder (str): The folder in which the metadata file is located.
Returns:
dict[str, dict[str, int]]: The size list dictionary.
dict[str, dict[str, int]]: The metadata dictionary.
"""
sizelist = {}
sizelist_path = os.path.join(folder, ".sizelist.json")
if not os.path.exists(sizelist_path):
logger.info("creating new size list file", extra={"file": sizelist_path})
with open(sizelist_path, "x", encoding="utf-8") as sizelistfile:
sizelistfile.write("{}")
with open(sizelist_path, "r+", encoding="utf-8") as sizelistfile:
logger.info("reading size list file", extra={"file": sizelist_path})
metadata = {}
metadata_path = os.path.join(folder, ".metadata.json")
if not os.path.exists(metadata_path):
logger.info("creating new metadata file", extra={"file": metadata_path})
with open(metadata_path, "x", encoding="utf-8") as metadatafile:
metadatafile.write("{}")
with open(metadata_path, "r+", encoding="utf-8") as metadatafile:
logger.info("reading metadata file", extra={"file": metadata_path})
try:
sizelist = json.loads(sizelistfile.read())
metadata = json.loads(metadatafile.read())
except json.decoder.JSONDecodeError:
logger.warning("invalid JSON in size list file", extra={"file": sizelist_path})
sizelist = {}
return sizelist
logger.warning("invalid JSON in metadata file", extra={"file": metadata_path})
metadata = {}
# remove old sizelist if it exists
sizelist_path = os.path.join(folder, ".sizelist.json")
if os.path.exists(sizelist_path):
logger.warning("found old .sizelist.json, removing it...", extra={"path": sizelist_path})
os.remove(sizelist_path)
return metadata
def update_sizelist(sizelist: dict[str, dict[str, Any]], folder: str) -> None:
def update_metadata(metadata: dict[str, dict[str, Any]], folder: str) -> None:
"""
Updates the size list JSON file.
Updates the metadata JSON file.
Args:
sizelist (dict[str, dict[str, int]]): The size list dictionary to be written to the file.
folder (str): The folder in which the size list file is located.
metadata (dict[str, dict[str, int]]): The metadata dictionary to be written to the file.
folder (str): The folder in which the metadata file is located.
"""
sizelist_path = os.path.join(folder, ".sizelist.json")
if sizelist:
with open(sizelist_path, "w", encoding="utf-8") as sizelistfile:
logger.info("writing size list file", extra={"file": sizelist_path})
sizelistfile.write(json.dumps(sizelist, indent=4))
metadata_path = os.path.join(folder, ".metadata.json")
if metadata:
with open(metadata_path, "w", encoding="utf-8") as metadatafile:
logger.info("writing metadata file", extra={"file": metadata_path})
metadatafile.write(json.dumps(metadata, indent=4))
else:
if os.path.exists(sizelist_path):
logger.info("deleting empty size list file", extra={"file": sizelist_path})
os.remove(sizelist_path)
if os.path.exists(metadata_path):
logger.info("deleting empty metadata file", extra={"file": metadata_path})
os.remove(metadata_path)
def get_image_info(item: str, folder: str) -> dict[str, Any]:
@@ -92,10 +97,17 @@ def get_image_info(item: str, folder: str) -> dict[str, Any]:
dict[str, Any]: A dictionary containing image width, height, and EXIF data.
"""
file = os.path.join(folder, item)
try:
with Image.open(file) as img:
logger.info("extracting image information", extra={"file": file})
exif = img.getexif()
width, height = img.size
exif = img.getexif()
xmpdata = img.getxmp()
except UnidentifiedImageError:
logger.error("cannot identify image file", extra={"file": file})
print(f"cannot identify image file: {file}")
return {"width": None, "height": None, "tags": None, "exifdata": None, "xmp": None}
if exif:
logger.info("extracting EXIF data", extra={"file": file})
ifd = exif.get_ifd(ExifTags.IFD.Exif)
@@ -117,9 +129,9 @@ def get_image_info(item: str, folder: str) -> dict[str, Any]:
content = newtuple
if tag in ["DateTime", "DateTimeOriginal", "DateTimeDigitized"]:
epr = r"\d{4}:\d{2}:\d{2} \d{2}:\d{2}:\d{2}"
if re.match(epr, content):
if re.match(epr, str(content)):
try:
content = datetime.strptime(content, "%Y:%m:%d %H:%M:%S").strftime("%Y-%m-%d %H:%M:%S")
content = datetime.strptime(str(content), "%Y:%m:%d %H:%M:%S").strftime("%Y-%m-%d %H:%M:%S")
except ValueError:
content = None
else:
@@ -131,12 +143,29 @@ def get_image_info(item: str, folder: str) -> dict[str, Any]:
for key in ["PrintImageMatching", "UserComment", "MakerNote"]:
if key in exifdata:
del exifdata[key]
return {"width": width, "height": height, "exifdata": exifdata}
else:
return {"width": width, "height": height, "exifdata": None}
exifdata = None
tags = []
xmp = None
if xmpdata:
if xmpdata.get("xmpmeta", False):
if isinstance(xmpdata["xmpmeta"]["RDF"]["Description"], dict):
if xmpdata["xmpmeta"]["RDF"]["Description"].get("subject", False):
tags = xmpdata["xmpmeta"]["RDF"]["Description"]["subject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
xmp = xmpdata
if xmpdata.get("xapmeta", False):
if isinstance(xmpdata["xapmeta"]["RDF"]["Description"], dict):
if xmpdata["xapmeta"]["RDF"]["Description"].get("subject", False):
tags = xmpdata["xapmeta"]["RDF"]["Description"]["subject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
xmp = xmpdata
return {"width": width, "height": height, "tags": tags, "exifdata": exifdata, "xmp": xmp}
def process_image(item: str, folder: str, _args: Args, baseurl: str, sizelist: dict[str, dict[str, int]], raw: list[str]) -> dict[str, Any]:
def process_image(item: str, folder: str, _args: Args, baseurl: str, metadata: dict[str, dict[str, int]], raw: list[str]) -> dict[str, Any]:
"""
Processes an image and prepares its data for the HTML template.
@@ -145,23 +174,25 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, sizelist: d
folder (str): The folder containing the image.
_args (Args): Parsed command line arguments.
baseurl (str): Base URL for the web root.
sizelist (dict[str, dict[str, int]]): dictionary containing size information for images.
metadata (dict[str, dict[str, int]]): dictionary containing size information for images.
raw (list[str]): list of raw image file extensions.
Returns:
dict[str, Any]: dictionary containing image details for HTML rendering.
"""
extsplit = os.path.splitext(item)
if item not in sizelist or _args.reread_metadata:
sizelist[item] = get_image_info(item, folder)
if item not in metadata or _args.reread_metadata:
metadata[item] = get_image_info(item, folder)
image = {
"url": f"{_args.web_root_url}{baseurl}{urllib.parse.quote(item)}",
"thumbnail": f"{_args.web_root_url}.thumbnails/{baseurl}{urllib.parse.quote(item)}.jpg",
"name": item,
"width": sizelist[item]["width"],
"height": sizelist[item]["height"],
"exifdata": sizelist[item].get("exifdata", ""),
"width": metadata[item]["width"],
"height": metadata[item]["height"],
"tags": metadata[item]["tags"],
"exifdata": metadata[item].get("exifdata", ""),
"xmp": metadata[item].get("xmp", ""),
}
path = os.path.join(_args.root_directory, ".thumbnails", baseurl, item + ".jpg")
if not os.path.exists(path) or _args.regenerate_thumbnails:
@@ -194,10 +225,10 @@ def generate_html(folder: str, title: str, _args: Args, raw: list[str], version:
"""
logger.info("processing folder", extra={"folder": folder})
if _args.regenerate_thumbnails:
if os.path.exists(os.path.join(folder, ".sizelist.json")):
logger.info("removing .sizelist.json", extra={"folder": folder})
os.remove(os.path.join(folder, ".sizelist.json"))
sizelist = initialize_sizelist(folder)
if os.path.exists(os.path.join(folder, ".metadata.json")):
logger.info("removing .metadata.json", extra={"folder": folder})
os.remove(os.path.join(folder, ".metadata.json"))
metadata = initialize_metadata(folder)
items = sorted(os.listdir(folder))
contains_files = False
@@ -218,7 +249,7 @@ def generate_html(folder: str, title: str, _args: Args, raw: list[str], version:
else:
contains_files = True
if os.path.splitext(item)[1].lower() in _args.file_extensions:
images.append(process_image(item, folder, _args, baseurl, sizelist, raw))
images.append(process_image(item, folder, _args, baseurl, metadata, raw))
if item == "info":
process_info_file(folder, item)
if item == "LICENSE":
@@ -231,13 +262,13 @@ def generate_html(folder: str, title: str, _args: Args, raw: list[str], version:
else:
contains_files = True
if os.path.splitext(item)[1].lower() in _args.file_extensions:
images.append(process_image(item, folder, _args, baseurl, sizelist, raw))
images.append(process_image(item, folder, _args, baseurl, metadata, raw))
if item == "info":
process_info_file(folder, item)
if item == "LICENSE":
process_license(folder, item)
update_sizelist(sizelist, folder)
update_metadata(metadata, folder)
if should_generate_html(images, contains_files, _args):
create_html_file(folder, title, foldername, images, subfolders, _args, version, logo)
@@ -261,7 +292,7 @@ def create_thumbnail_folder(foldername: str, root_directory: str) -> None:
os.mkdir(thumbnails_path)
def process_subfolder(item: str, folder: str, baseurl: str, subfolders: list[dict[str, str]], _args: Args, raw: list[str], version: str, logo: str) -> None:
def process_subfolder(item: str, folder: str, baseurl: str, subfolders: list[dict[str, str | None]], _args: Args, raw: list[str], version: str, logo: str) -> None:
"""
Processes a subfolder.
@@ -366,6 +397,12 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
else None
)
alltags = set()
for img in images:
for tag in img["tags"]:
alltags.add(tag)
alltags = sorted(alltags)
folder_info = info.get(urllib.parse.quote(folder), "").split("\n")
_info = [i for i in folder_info if len(i) > 1] if folder_info else None
if _args.reverse_sort:
@@ -414,6 +451,7 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
version=version,
logo=logo,
licensefile=license_url,
tags=alltags,
)
with open(html_file, "w", encoding="utf-8") as f:
@@ -421,7 +459,7 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
f.write(content)
def list_folder(folder: str, title: str, _args: Args, raw: list[str], version: str, logo: str) -> list[tuple[str, str]]:
def list_folder(folder: str, title: str, _args: Args, raw: list[str], version: str, logo: str) -> list[tuple[str, str, str]]:
"""
lists and processes a folder, generating HTML files.

View File

@@ -52,6 +52,10 @@
background-color: var(--color2);
}
.tagentry:hover {
background-color: var(--color4);
}
.column img {
background-color: var(--color2);
}

View File

@@ -31,23 +31,38 @@
<body>
<div class="header">
<ul class="navbar">
<ol class="navbar">
<div class="navleft">
<li><a href="{{ root }}">Home</a></li>
{%- if parent %}
<li><a href="{{ parent }}">Parent Directory</a></li>
{%- endif %}
{%- if info %}
<li class="tooltip"><a>Info</a><span class="tooltiptext">
<li class="tooltip"><a>Info</a><span class="tooltiptext infotext">
{%- for infoline in info -%}
{{ infoline }}<br />
{%- endfor -%}
</span></li>
{%- endif %}
</div>
<div class="navcenter">
<li class="title"><span class="header">{{ header }}</span></li>
</div>
<div class="navright">
{%- if tags|length > 0 %}
<li class="tooltip"><a>Filter by Tags</a>
<ol class="tooltiptext tagdropdown" id="tagdropdown">
{%- for tag in tags -%}
<li class="tagentry"><label onclick="filter()"><input type="checkbox" />{{ tag }}</label></li><br />
{%- endfor -%}
</ol>
</li>
{%- endif %}
{%- if licensefile %}
<li class="license"><a href="{{ licensefile }}">License</a></li>
{%- endif %}
</ul>
</div>
</ol>
{% if subdirectories %}
{%- for subdirectory in subdirectories %}
<link rel="preload" href="{{ subdirectory.url }}/index.html" type="text/html">
@@ -69,7 +84,7 @@
</div>
{% if images %}
{%- set ns = namespace(count = 0) -%}
<div class="row">
<div class="row" id="imagelist">
{%- for image in images %}
<div class="column">
<figure>
@@ -160,9 +175,9 @@
var items = [
{%- for image in images %}
{%- if image.exifdata.DateTime %}
{ src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}", title: "Captured: {{ image.exifdata.DateTime }}" },
{ src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}", tags: "{{ image.tags }}", title: "Captured: {{ image.exifdata.DateTime }}" },
{%- else %}
{ src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}" },
{ src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}", tags: "{{ image.tags }}" },
{%- endif %}
{%- endfor %}
];
@@ -207,13 +222,42 @@
fetch(urlToFetch, {
method: 'get',
signal: signal,
}).catch(function (err) {});
}).catch(function (err) { });
}
function cancel(img) {
controllers[img].abort();
delete controllers[img];
}
{%- if tags|length > 0 %}
function filter() {
var selected_tags = [];
var tagdropdown, imagelist, figures, i, j, tags, incl;
tagdropdown = document.getElementById("tagdropdown").getElementsByTagName("li");
for (i = 0; i < tagdropdown.length; i++) {
if (tagdropdown[i].firstChild.firstChild.checked) {
selected_tags.push([tagdropdown[i].innerText])
}
}
imagelist = document.getElementById("imagelist");
figures = imagelist.getElementsByTagName("div");
for (i = 0; i < figures.length; i++) {
tags = items[i].tags;
incl = true;
for (j = 0; j < selected_tags.length; j++) {
if (tags.indexOf(selected_tags[j]) == -1) {
incl = false;
}
}
if (incl || selected_tags == []) {
figures[i].style.display = "";
} else {
figures[i].style.display = "none";
}
}
}
{%- endif %}
</script>
{%- endif %}
</body>

BIN
test/example/DSC00009.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 MiB

BIN
test/example/DSC01106.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

View File

@@ -6,18 +6,18 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>example - LICENSE</title>
<link rel="manifest" href="/.static/manifest.json">
<link rel="preload" href="file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/test/.static/global.css" as="style">
<link rel="preload" href="file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/test/.static/theme.css" as="style">
<link rel="icon" type="image/x-icon" href="file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/test/.static/favicon.ico">
<link rel="stylesheet" href="file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/test/.static/global.css">
<link rel="stylesheet" href="file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/test/.static/theme.css">
<link rel="preload" href="file:///home/user/git/github.com/greflm13/simple-picture-server/test/.static/global.css" as="style">
<link rel="preload" href="file:///home/user/git/github.com/greflm13/simple-picture-server/test/.static/theme.css" as="style">
<link rel="icon" type="image/x-icon" href="file:///home/user/git/github.com/greflm13/simple-picture-server/test/.static/favicon.ico">
<link rel="stylesheet" href="file:///home/user/git/github.com/greflm13/simple-picture-server/test/.static/global.css">
<link rel="stylesheet" href="file:///home/user/git/github.com/greflm13/simple-picture-server/test/.static/theme.css">
</head>
<body>
<div class="header">
<ul class="navbar">
<li><a href="file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/test/">Home</a></li>
<li><a href="file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/test/example/">Parent Directory</a></li>
<li><a href="file:///home/user/git/github.com/greflm13/simple-picture-server/test/">Home</a></li>
<li><a href="file:///home/user/git/github.com/greflm13/simple-picture-server/test/example/">Parent Directory</a></li>
<li class="title"><span class="header">example - LICENSE</span></li>
</ul>
</div>
@@ -453,14 +453,14 @@ Creative Commons may be contacted at creativecommons.org.</br>
</div>
<div class="footer" xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/">
<a property="dct:title" rel="cc:attributionURL" href="file:///home/user/git/github.com/greflm13/StaticGalleryBuilder/test/">Pictures</a> by <span property="cc:attributionName">Author</span> is licensed under
<a property="dct:title" rel="cc:attributionURL" href="file:///home/user/git/github.com/greflm13/simple-picture-server/test/">Pictures</a> by <span property="cc:attributionName">Author</span> is licensed under
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="license noopener noreferrer">CC BY-NC-SA 4.0
<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" alt="" />
<img src="https://mirrors.creativecommons.org/presskit/icons/by.svg" alt="" />
<img src="https://mirrors.creativecommons.org/presskit/icons/nc.svg" alt="" />
<img src="https://mirrors.creativecommons.org/presskit/icons/sa.svg" alt="" />
</a>
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder 2.5.0</a> by <a
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder 2.7.1</a> by <a
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer"><svg width="48.858002mm" height="21.24mm" viewBox="0 0 48.858002 21.24" version="1.1" id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <text xml:space="preserve" x="21.124891" y="17.106812" font-style="normal" font-variant="normal" font-weight="300" font-stretch="condensed" font-size="2.46944px" line-height="9.33327px" font-family="Barlow Condensed Light" fill="#6e6e6e" stroke-width="1" fill-opacity="1">sorogon</text> <text xml:space="preserve" x="2.3734004" y="14.853325" font-style="normal" font-variant="normal" font-weight="250" font-stretch="condensed" font-size="16.9333px" line-height="63.9997px" font-family="Barlow Condensed Thin" fill="#6e6e6e" stroke-width="1" fill-opacity="1">&lt;/srgn&gt;</text> </svg></a>.</span>
<button onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
</div>

View File

@@ -97,6 +97,10 @@ body {
background-color: var(--color6);
}
.tagentry:hover {
background-color: var(--color3);
}
.column img {
background-color: var(--bcolor2);
}

View File

@@ -96,6 +96,10 @@ body {
background-color: var(--color3);
}
.tagentry:hover {
background-color: var(--color7);
}
.column img {
background-color: var(--bcolor1);
}

View File

@@ -74,6 +74,10 @@
background-color: var(--bcolor1);
}
.tagentry:hover {
background-color: var(--bcolor3);
}
.column img {
background-color: var(--bcolor1);
}

View File

@@ -74,6 +74,10 @@
background-color: var(--bcolor1);
}
.tagentry:hover {
background-color: var(--bcolor3);
}
.column img {
background-color: var(--bcolor1);
}

View File

@@ -79,6 +79,10 @@
font-family: "Playfair Display", serif;
}
.tagentry:hover {
background-color: var(--color3);
}
.column img {
background-color: var(--bcolor4);
}

View File

@@ -73,6 +73,10 @@
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor4);
}

View File

@@ -79,6 +79,10 @@
font-family: "Nunito", sans-serif;
}
.tagentry:hover {
background-color: var(--color4);
}
.column img {
background-color: var(--bcolor4);
}

View File

@@ -73,6 +73,10 @@
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor4);
}

View File

@@ -52,6 +52,10 @@
background-color: var(--color3);
}
.tagentry:hover {
background-color: var(--color4);
}
.column img {
background-color: var(--bcolor2);
}

View File

@@ -52,6 +52,10 @@
background-color: var(--color2);
}
.tagentry:hover {
background-color: var(--color4);
}
.column img {
background-color: var(--color2);
}

View File

@@ -73,6 +73,10 @@
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor4);
}

View File

@@ -72,6 +72,10 @@
background-color: var(--color3);
}
.tagentry:hover {
background-color: var(--color2);
}
.column img {
background-color: var(--bcolor3);
}

View File

@@ -55,6 +55,10 @@
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor3);
}
.column img {
background-color: var(--bcolor5);
}

View File

@@ -76,6 +76,10 @@
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor5);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -79,6 +79,10 @@
font-family: "Lora", serif;
}
.tagentry:hover {
background-color: var(--bcolor3);
}
.column img {
background-color: var(--bcolor4);
}

View File

@@ -80,6 +80,10 @@
background-color: var(--color4);
}
.tagentry:hover {
background-color: var(--color3);
}
.column img {
background-color: var(--color4);
}

View File

@@ -80,6 +80,11 @@
font-family: "Roboto", sans-serif;
}
.tagentry:hover {
background-color: var(--bcolor3);
color: var(--bcolor2);
}
.column img {
background-color: var(--bcolor4);
}

View File

@@ -74,6 +74,10 @@
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor4);
}

View File

@@ -88,6 +88,10 @@
font-family: "Montserrat", sans-serif;
}
.tagentry:hover {
background-color: var(--color2);
}
.column img {
background-color: var(--bcolor4);
}