mirror of
https://github.com/greflm13/StaticGalleryBuilder.git
synced 2026-02-05 19:19:29 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
67a9058b7d
|
|||
|
79e34d7e43
|
|||
|
0cda1706fa
|
|||
|
85467f4f1e
|
|||
|
00ccb96581
|
|||
|
5227beb02a
|
@@ -38,8 +38,8 @@
|
||||
"-n",
|
||||
"-m",
|
||||
"--reverse-sort",
|
||||
// "--regenerate-thumbnails",
|
||||
// "--reread-metadata",
|
||||
"--regenerate-thumbnails",
|
||||
"--reread-metadata",
|
||||
"--folderthumbnails"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
|
||||
@@ -176,6 +176,7 @@ figure {
|
||||
|
||||
.tooltip .tagdropdown {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
@@ -196,6 +197,11 @@ figure {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext ol {
|
||||
margin-left: 0;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext .tagentry label {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
|
||||
@@ -5,6 +5,7 @@ import fnmatch
|
||||
import json
|
||||
from typing import Any
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
|
||||
from tqdm.auto import tqdm
|
||||
from PIL import Image, ExifTags, TiffImagePlugin, UnidentifiedImageError
|
||||
@@ -97,8 +98,32 @@ def initialize_metadata(folder: str) -> dict[str, dict[str, int]]:
|
||||
# remove old sizelist if it exists
|
||||
sizelist_path = os.path.join(folder, ".sizelist.json")
|
||||
if os.path.exists(sizelist_path):
|
||||
with open(sizelist_path, "r") as sizelist:
|
||||
metadata = json.loads(sizelist.read())
|
||||
logger.warning("found old .sizelist.json, removing it...", extra={"path": sizelist_path})
|
||||
os.remove(sizelist_path)
|
||||
|
||||
# convert from old metadata format
|
||||
if "images" not in metadata and "subfolders" not in metadata:
|
||||
images = metadata.copy()
|
||||
metadata = {}
|
||||
metadata["images"] = images
|
||||
elif "images" not in metadata:
|
||||
metadata["images"] = {}
|
||||
for k, v in metadata["images"].items():
|
||||
if "width" in v:
|
||||
metadata["images"][k]["w"] = v["width"]
|
||||
del metadata["images"][k]["width"]
|
||||
if "height" in v:
|
||||
metadata["images"][k]["h"] = v["height"]
|
||||
del metadata["images"][k]["height"]
|
||||
if "tags" not in v:
|
||||
metadata["images"][k]["tags"] = []
|
||||
if "exifdata" not in v:
|
||||
metadata["images"][k]["exifdata"] = None
|
||||
if "xmp" not in v:
|
||||
metadata["images"][k]["xmp"] = None
|
||||
|
||||
return metadata
|
||||
|
||||
|
||||
@@ -143,7 +168,7 @@ def get_image_info(item: str, folder: str) -> dict[str, Any]:
|
||||
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}
|
||||
return {"w": None, "h": None, "tags": None, "exifdata": None, "xmp": None}
|
||||
if exif:
|
||||
logger.info("extracting EXIF data", extra={"file": file})
|
||||
ifd = exif.get_ifd(ExifTags.IFD.Exif)
|
||||
@@ -189,23 +214,64 @@ def get_image_info(item: str, folder: str) -> dict[str, Any]:
|
||||
tags = xmpdata["xmpmeta"]["RDF"]["Description"]["subject"]["Bag"]["li"]
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
xmp = xmpdata
|
||||
except TypeError:
|
||||
...
|
||||
pass
|
||||
except KeyError:
|
||||
...
|
||||
pass
|
||||
try:
|
||||
tags = xmpdata["xapmeta"]["RDF"]["Description"]["subject"]["Bag"]["li"]
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
xmp = xmpdata
|
||||
except TypeError:
|
||||
...
|
||||
pass
|
||||
except KeyError:
|
||||
...
|
||||
pass
|
||||
try:
|
||||
tags = xmpdata["xmpmeta"]["RDF"]["Description"]["hierarchicalSubject"]["Bag"]["li"]
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
except TypeError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
tags = xmpdata["xapmeta"]["RDF"]["Description"]["hierarchicalSubject"]["Bag"]["li"]
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
except TypeError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
if None in tags:
|
||||
tags.remove(None)
|
||||
return {"width": width, "height": height, "tags": tags, "exifdata": exifdata, "xmp": xmp}
|
||||
return {"w": width, "h": height, "tags": tags, "exifdata": exifdata, "xmp": xmp}
|
||||
|
||||
|
||||
def nested_dict():
|
||||
return defaultdict(nested_dict)
|
||||
|
||||
|
||||
def insert_path(d, path):
|
||||
for part in path[:-1]:
|
||||
d = d[part]
|
||||
last = path[-1]
|
||||
if not isinstance(d[last], dict):
|
||||
d[last] = {}
|
||||
|
||||
|
||||
def finalize(d):
|
||||
if isinstance(d, defaultdict):
|
||||
# Sort keys before recursion
|
||||
return {k: finalize(d[k]) for k in sorted(d)}
|
||||
return d or []
|
||||
|
||||
|
||||
def parse_hierarchical_tags(tags, delimiter="|"):
|
||||
tree = nested_dict()
|
||||
for tag in tags:
|
||||
parts = tag.split(delimiter)
|
||||
insert_path(tree, parts)
|
||||
return finalize(tree)
|
||||
|
||||
|
||||
def get_tags(sidecarfile: str) -> list[str]:
|
||||
@@ -228,17 +294,33 @@ def get_tags(sidecarfile: str) -> list[str]:
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
except TypeError:
|
||||
...
|
||||
pass
|
||||
except KeyError:
|
||||
...
|
||||
pass
|
||||
try:
|
||||
tags = xmpdata["xapmeta"]["RDF"]["Description"]["subject"]["Bag"]["li"]
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
except TypeError:
|
||||
...
|
||||
pass
|
||||
except KeyError:
|
||||
...
|
||||
pass
|
||||
try:
|
||||
tags = xmpdata["xmpmeta"]["RDF"]["Description"]["hierarchicalSubject"]["Bag"]["li"]
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
except TypeError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
tags = xmpdata["xapmeta"]["RDF"]["Description"]["hierarchicalSubject"]["Bag"]["li"]
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
except TypeError:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
if None in tags:
|
||||
tags.remove(None)
|
||||
return tags
|
||||
@@ -260,25 +342,23 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, metadata: d
|
||||
dict[str, Any]: dictionary containing image details for HTML rendering.
|
||||
"""
|
||||
extsplit = os.path.splitext(item)
|
||||
if item not in metadata or _args.reread_metadata:
|
||||
metadata[item] = get_image_info(item, folder)
|
||||
if item not in metadata["images"] or _args.reread_metadata:
|
||||
metadata["images"][item] = get_image_info(item, folder)
|
||||
sidecarfile = os.path.join(folder, item + ".xmp")
|
||||
if os.path.exists(sidecarfile):
|
||||
logger.info("xmp sidecar file found", extra={"file": sidecarfile})
|
||||
try:
|
||||
metadata[item]["tags"] = get_tags(sidecarfile)
|
||||
metadata["images"][item]["tags"] = get_tags(sidecarfile)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
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",
|
||||
"src": f"{_args.web_root_url}{baseurl}{urllib.parse.quote(item)}",
|
||||
"msrc": f"{_args.web_root_url}.thumbnails/{baseurl}{urllib.parse.quote(item)}.jpg",
|
||||
"name": item,
|
||||
"width": metadata[item]["width"],
|
||||
"height": metadata[item]["height"],
|
||||
"tags": metadata[item]["tags"],
|
||||
"exifdata": metadata[item].get("exifdata", ""),
|
||||
"xmp": metadata[item].get("xmp", ""),
|
||||
"w": metadata["images"][item]["w"],
|
||||
"h": metadata["images"][item]["h"],
|
||||
"tags": metadata["images"][item]["tags"],
|
||||
}
|
||||
path = os.path.join(_args.root_directory, ".thumbnails", baseurl, item + ".jpg")
|
||||
if not os.path.exists(path) or _args.regenerate_thumbnails:
|
||||
@@ -296,10 +376,13 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, metadata: d
|
||||
else:
|
||||
logger.info("raw file found", extra={"file": file, "extension": _raw})
|
||||
image["raw"] = url
|
||||
return image
|
||||
|
||||
metadata["images"][item].update(image)
|
||||
|
||||
return image, metadata
|
||||
|
||||
|
||||
def generate_html(folder: str, title: str, _args: Args, raw: list[str], version: str, logo) -> None:
|
||||
def generate_html(folder: str, title: str, _args: Args, raw: list[str], version: str, logo) -> list[str]:
|
||||
"""
|
||||
Generates HTML content for a folder of images.
|
||||
|
||||
@@ -315,15 +398,23 @@ def generate_html(folder: str, title: str, _args: Args, raw: list[str], version:
|
||||
logger.info("removing .metadata.json", extra={"folder": folder})
|
||||
os.remove(os.path.join(folder, ".metadata.json"))
|
||||
metadata = initialize_metadata(folder)
|
||||
if _args.reverse_sort:
|
||||
items = sorted(os.listdir(folder), reverse=True)
|
||||
else:
|
||||
items = sorted(os.listdir(folder))
|
||||
|
||||
contains_files = False
|
||||
images = []
|
||||
subfolders = []
|
||||
subfoldertags = []
|
||||
foldername = folder.removeprefix(_args.root_directory)
|
||||
foldername = f"{foldername}/" if foldername else ""
|
||||
baseurl = urllib.parse.quote(foldername)
|
||||
|
||||
gone = [item for item in metadata["images"] if item not in items]
|
||||
for gon in gone:
|
||||
del metadata["images"][gon]
|
||||
|
||||
create_thumbnail_folder(foldername, _args.root_directory)
|
||||
|
||||
logger.info("processing contents", extra={"folder": folder})
|
||||
@@ -331,11 +422,12 @@ def generate_html(folder: str, title: str, _args: Args, raw: list[str], version:
|
||||
for item in tqdm(items, total=len(items), desc=f"Getting image infos - {folder}", unit="files", ascii=True, dynamic_ncols=True):
|
||||
if item not in EXCLUDES and not item.startswith("."):
|
||||
if os.path.isdir(os.path.join(folder, item)):
|
||||
process_subfolder(item, folder, baseurl, subfolders, _args, raw, version, logo)
|
||||
subfoldertags = process_subfolder(item, folder, baseurl, subfolders, _args, raw, version, logo)
|
||||
else:
|
||||
contains_files = True
|
||||
if os.path.splitext(item)[1].lower() in _args.file_extensions:
|
||||
images.append(process_image(item, folder, _args, baseurl, metadata, raw))
|
||||
img, metadata = process_image(item, folder, _args, baseurl, metadata, raw)
|
||||
images.append(img)
|
||||
if item == "info":
|
||||
process_info_file(folder, item)
|
||||
if item == "LICENSE":
|
||||
@@ -344,24 +436,27 @@ def generate_html(folder: str, title: str, _args: Args, raw: list[str], version:
|
||||
for item in items:
|
||||
if item not in EXCLUDES and not item.startswith("."):
|
||||
if os.path.isdir(os.path.join(folder, item)):
|
||||
process_subfolder(item, folder, baseurl, subfolders, _args, raw, version, logo)
|
||||
subfoldertags = process_subfolder(item, folder, baseurl, subfolders, _args, raw, version, logo)
|
||||
else:
|
||||
contains_files = True
|
||||
if os.path.splitext(item)[1].lower() in _args.file_extensions:
|
||||
images.append(process_image(item, folder, _args, baseurl, metadata, raw))
|
||||
img, metadata = process_image(item, folder, _args, baseurl, metadata, raw)
|
||||
images.append(img)
|
||||
if item == "info":
|
||||
process_info_file(folder, item)
|
||||
if item == "LICENSE":
|
||||
process_license(folder, item)
|
||||
|
||||
metadata["subfolders"] = subfolders
|
||||
update_metadata(metadata, folder)
|
||||
|
||||
if should_generate_html(images, contains_files, _args):
|
||||
create_html_file(folder, title, foldername, images, subfolders, _args, version, logo)
|
||||
subfoldertags = create_html_file(folder, title, foldername, images, subfolders, _args, version, logo, subfoldertags)
|
||||
else:
|
||||
if os.path.exists(os.path.join(folder, "index.html")):
|
||||
logger.info("removing existing index.html", extra={"folder": folder})
|
||||
os.remove(os.path.join(folder, "index.html"))
|
||||
return subfoldertags
|
||||
|
||||
|
||||
def create_thumbnail_folder(foldername: str, root_directory: str) -> None:
|
||||
@@ -378,7 +473,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 | None]], _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) -> list[str]:
|
||||
"""
|
||||
Processes a subfolder.
|
||||
|
||||
@@ -404,10 +499,11 @@ def process_subfolder(item: str, folder: str, baseurl: str, subfolders: list[dic
|
||||
else:
|
||||
thumb = f"{_args.web_root_url}.thumbnails/{baseurl}{urllib.parse.quote(item)}/{urllib.parse.quote(thumbitems[0])}.jpg"
|
||||
|
||||
subfolders.append({"url": subfolder_url, "name": item, "thumb": thumb})
|
||||
subfolders.append({"url": subfolder_url, "name": item, "thumb": thumb, "metadata": f"{_args.web_root_url}{baseurl}{urllib.parse.quote(item)}/.metadata.json"})
|
||||
if item not in _args.exclude_folders:
|
||||
if not any(fnmatch.fnmatchcase(os.path.join(folder, item), exclude) for exclude in _args.exclude_folders):
|
||||
generate_html(os.path.join(folder, item), os.path.join(folder, item).removeprefix(_args.root_directory), _args, raw, version, logo)
|
||||
return generate_html(os.path.join(folder, item), os.path.join(folder, item).removeprefix(_args.root_directory), _args, raw, version, logo)
|
||||
return []
|
||||
|
||||
|
||||
def process_license(folder: str, item: str) -> None:
|
||||
@@ -452,7 +548,9 @@ def should_generate_html(images: list[dict[str, Any]], contains_files, _args: Ar
|
||||
return images or (_args.use_fancy_folders and not contains_files) or (_args.use_fancy_folders and _args.ignore_other_files)
|
||||
|
||||
|
||||
def create_html_file(folder: str, title: str, foldername: str, images: list[dict[str, Any]], subfolders: list[dict[str, str]], _args: Args, version: str, logo: str) -> None:
|
||||
def create_html_file(
|
||||
folder: str, title: str, foldername: str, images: list[dict[str, Any]], subfolders: list[dict[str, str]], _args: Args, version: str, logo: str, subfoldertags: list[str]
|
||||
) -> list[str]:
|
||||
"""
|
||||
Creates the HTML file using the template.
|
||||
|
||||
@@ -485,9 +583,9 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
|
||||
|
||||
alltags = set()
|
||||
for img in images:
|
||||
for tag in img["tags"]:
|
||||
alltags.add(tag)
|
||||
alltags = sorted(alltags)
|
||||
alltags.update(img["tags"])
|
||||
|
||||
alltags.update(set(subfoldertags))
|
||||
|
||||
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
|
||||
@@ -531,19 +629,28 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
|
||||
header=header,
|
||||
license=license_info,
|
||||
subdirectories=subfolders,
|
||||
images=images,
|
||||
info=_info,
|
||||
webmanifest=_args.generate_webmanifest,
|
||||
version=version,
|
||||
logo=logo,
|
||||
licensefile=license_url,
|
||||
tags=alltags,
|
||||
tags=parse_hierarchical_tags(alltags),
|
||||
)
|
||||
|
||||
with open(html_file, "w", encoding="utf-8") as f:
|
||||
logger.info("writing html file", extra={"path": html_file})
|
||||
f.write(content)
|
||||
|
||||
if len(subfoldertags) > 1 and len(alltags) > 1:
|
||||
alltags.update(set(subfoldertags))
|
||||
return sorted(alltags)
|
||||
elif len(subfoldertags) > 1:
|
||||
return sorted(subfoldertags)
|
||||
elif len(alltags) > 1:
|
||||
return sorted(alltags)
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def list_folder(folder: str, title: str, _args: Args, raw: list[str], version: str, logo: str) -> list[tuple[str, str, str]]:
|
||||
"""
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
background-color: var(--color2);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color4);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
{%- macro render_tags(tag_tree, parent) -%}
|
||||
<ol>
|
||||
{%- for key, value in tag_tree.items() %}
|
||||
<li class="tagentry">
|
||||
<label onclick="filter()" title="{{ key }}" id="{{ parent }}|{{ key }}">
|
||||
<input class="tag" type="checkbox" />{{ key }}
|
||||
</label>
|
||||
{%- if value %}
|
||||
{{ render_tags(value, parent + '|' + key) }}
|
||||
{%- endif %}
|
||||
</li>
|
||||
{%- endfor %}
|
||||
</ol>
|
||||
{%- endmacro -%}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
@@ -21,12 +35,10 @@
|
||||
<link rel="preload" href="{{ root }}.static/pswp/default-skin/default-skin.css" as="style">
|
||||
<link rel="modulepreload" href="{{ root }}.static/pswp/photoswipe.min.js">
|
||||
<link rel="modulepreload" href="{{ root }}.static/pswp/photoswipe-ui-default.min.js">
|
||||
{%- if images %}
|
||||
<link rel="stylesheet" href="{{ root }}.static/pswp/photoswipe.css">
|
||||
<link rel="stylesheet" href="{{ root }}.static/pswp/default-skin/default-skin.css">
|
||||
<script src="{{ root }}.static/pswp/photoswipe.min.js"></script>
|
||||
<script src="{{ root }}.static/pswp/photoswipe-ui-default.min.js"></script>
|
||||
{%- endif %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -49,15 +61,19 @@
|
||||
<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>
|
||||
{% if tags %}
|
||||
<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 -%}
|
||||
<span class="tagentry">
|
||||
<label onclick="recursive()">
|
||||
<input type="checkbox" id="recursive" />recursive filter
|
||||
</label>
|
||||
</span>
|
||||
{{ render_tags(tags, '') }}
|
||||
</ol>
|
||||
</li>
|
||||
{%- endif %}
|
||||
{% endif %}
|
||||
{%- if licensefile %}
|
||||
<li class="license"><a href="{{ licensefile }}">License</a></li>
|
||||
{%- endif %}
|
||||
@@ -82,27 +98,8 @@
|
||||
</div>
|
||||
{%- endif %}
|
||||
</div>
|
||||
{% if images %}
|
||||
{%- set ns = namespace(count = 0) -%}
|
||||
<div class="row" id="imagelist">
|
||||
{%- for image in images %}
|
||||
<div class="column">
|
||||
<figure>
|
||||
<img src="{{ image.thumbnail }}" alt="{{ image.name }}" onclick="openSwipe({{ ns.count }})" onmouseover="prefetch({{ ns.count }})" onmouseleave="cancel({{ ns.count }})" />
|
||||
{%- set ns.count = ns.count + 1 %}
|
||||
<figcaption class="caption">{{ image.name }}
|
||||
{%- if image.tiff %}
|
||||
<a href="{{ image.tiff }}">TIFF</a>
|
||||
{%- endif %}
|
||||
{%- if image.raw %}
|
||||
<a href="{{ image.raw }}">RAW</a>
|
||||
{%- endif %}
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
{%- endfor %}
|
||||
</div>
|
||||
{%- endif %}
|
||||
{% if license %}
|
||||
{%- if 'CC' in license.type %}
|
||||
<div class="footer" xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/">
|
||||
@@ -123,17 +120,16 @@
|
||||
{%- endif %}
|
||||
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder {{ version }}</a> by <a
|
||||
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer">{{ logo }}</a>.</span>
|
||||
<button onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
||||
<button type="button" onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
<div class="footer">
|
||||
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder {{ version }}</a> by <a
|
||||
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer">{{ logo }}</a>.</span>
|
||||
<button onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
||||
<button type="button" onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{% if images %}
|
||||
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="pswp__bg"></div>
|
||||
<div class="pswp__scroll-wrap">
|
||||
@@ -171,33 +167,86 @@
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var pswpElement = document.querySelectorAll('.pswp')[0];
|
||||
var items = [
|
||||
{%- for image in images %}
|
||||
{%- if 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 }}", tags: "{{ image.tags }}" },
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
];
|
||||
var re = /pid=(\d+)/;
|
||||
var controllers = {}
|
||||
const pswpElement = document.querySelectorAll('.pswp')[0];
|
||||
const re = /pid=(\d+)/;
|
||||
const filterre = /#(.*)/;
|
||||
let items = [];
|
||||
let shown = [];
|
||||
let subfolders = [];
|
||||
let controllers = {};
|
||||
let currentFolder = "";
|
||||
|
||||
function openSwipe(img) {
|
||||
var options = {
|
||||
index: img
|
||||
};
|
||||
var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
|
||||
gallery.init();
|
||||
function requestMetadata() {
|
||||
fetch(".metadata.json").then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
items = Object.values(data.images);
|
||||
subfolders = data.subfolders;
|
||||
if (filterre.test(window.location.href)) {
|
||||
var selected = window.location.href.match(filterre)[1].split(",");
|
||||
setFilter(selected);
|
||||
}
|
||||
filter();
|
||||
|
||||
if (re.test(window.location.href)) {
|
||||
var pid = window.location.href.match(re)[1];
|
||||
openSwipe(parseInt(pid));
|
||||
}
|
||||
|
||||
let totopbutton = document.getElementById("totop");
|
||||
if (items == []) {
|
||||
document.getElementById("imagelist").style.display = "none"
|
||||
} else {
|
||||
document.getElementById("imagelist").style.display = ""
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Failed to fetch data:', error));
|
||||
}
|
||||
|
||||
function openSwipe(img) {
|
||||
const options = {
|
||||
index: img
|
||||
};
|
||||
const gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, shown, options);
|
||||
gallery.init();
|
||||
}
|
||||
|
||||
async function recursive(sub = undefined) {
|
||||
const ischecked = document.getElementById("recursive").checked;
|
||||
const folders = document.getElementsByClassName("folders")[0];
|
||||
if (sub == undefined) {
|
||||
sub = subfolders;
|
||||
}
|
||||
if (ischecked) {
|
||||
if (folders != undefined) {
|
||||
folders.style.display = "none";
|
||||
}
|
||||
for (const folder of sub) {
|
||||
currentFolder = folder.name;
|
||||
try {
|
||||
const response = await fetch(folder.metadata);
|
||||
const data = await response.json();
|
||||
if (data.subfolders.length > 0) {
|
||||
await recursive(data.subfolders);
|
||||
}
|
||||
items = items.concat(Object.values(data.images));
|
||||
filter();
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch folder metadata:', error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (folders != undefined) {
|
||||
folders.style.display = "";
|
||||
}
|
||||
requestMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
const totopbutton = document.getElementById("totop");
|
||||
|
||||
window.onscroll = function () { scrollFunction() };
|
||||
|
||||
@@ -213,6 +262,23 @@
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
function updateImageList() {
|
||||
let str = ""
|
||||
let imagelist = document.getElementById("imagelist");
|
||||
shown.forEach((item, index) => {
|
||||
str += '<div class="column"><figure><img src="' + item.msrc + '" onclick="openSwipe(' + index + ')" onmouseover="prefetch(' + index + ')" onmouseleave="cancel(' + index + ')" /><figcaption class="caption">' + item.name;
|
||||
if (item.tiff != "" & item.tiff != undefined) {
|
||||
str += ' <a href="' + item.tiff + '">TIFF</a>';
|
||||
}
|
||||
if (item.raw != "" & item.raw != undefined) {
|
||||
str += ' <a href="' + item.raw + '">RAW</a>';
|
||||
}
|
||||
str += '</figcaption></figure></div>';
|
||||
});
|
||||
|
||||
imagelist.innerHTML = str;
|
||||
}
|
||||
|
||||
function prefetch(img) {
|
||||
const controller = new AbortController()
|
||||
const signal = controller.signal
|
||||
@@ -230,38 +296,59 @@
|
||||
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";
|
||||
shown = [];
|
||||
|
||||
window.location.href = window.location.href.split("#")[0] + "#";
|
||||
|
||||
const selected_tags = [];
|
||||
const tagcheckboxes = document.querySelectorAll("#tagdropdown input[class='tag']:checked");
|
||||
|
||||
tagcheckboxes.forEach((checkbox) => {
|
||||
const tag = checkbox.parentElement.id.trim().substring(1);
|
||||
selected_tags.push(tag);
|
||||
});
|
||||
|
||||
const urltags = selected_tags.join(",");
|
||||
|
||||
const isRecursiveChecked = document.getElementById("recursive").checked;
|
||||
|
||||
for (const item of items) {
|
||||
const tags = item.tags || [];
|
||||
const include = selected_tags.every(tag => tags.some(t => t.startsWith(tag)));
|
||||
|
||||
if (include || selected_tags.length === 0) {
|
||||
if (isRecursiveChecked || item.folder === currentFolder) {
|
||||
shown.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filter()
|
||||
{%- endif %}
|
||||
updateImageList();
|
||||
window.location.href += urltags;
|
||||
}
|
||||
|
||||
|
||||
function setFilter(selected) {
|
||||
tagdropdown = document.getElementById("tagdropdown").getElementsByTagName("li");
|
||||
selected.forEach((tag) => {
|
||||
for (var i = 0; i < tagdropdown.length; i++) {
|
||||
if (tagdropdown[i].innerText == tag) {
|
||||
tagdropdown[i].firstChild.firstChild.checked = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onLoad() {
|
||||
requestMetadata();
|
||||
}
|
||||
|
||||
window.addEventListener ?
|
||||
window.addEventListener("load", onLoad, false) :
|
||||
window.attachEvent && window.attachEvent("onload", onLoad);
|
||||
|
||||
</script>
|
||||
{%- endif %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -54,14 +54,14 @@
|
||||
{%- endif %}
|
||||
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder {{ version }}</a> by <a
|
||||
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer">{{ logo }}</a>.</span>
|
||||
<button onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
||||
<button type="button" onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
<div class="footer">
|
||||
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder {{ version }}</a> by <a
|
||||
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer">{{ logo }}</a>.</span>
|
||||
<button onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
||||
<button type="button" onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
|
||||
</div>
|
||||
{%- endif %}
|
||||
</body>
|
||||
@@ -2,6 +2,6 @@
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC00009.jpg">
|
||||
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>aqueduct</rdf:li><rdf:li>arch</rdf:li><rdf:li>arch bridge</rdf:li><rdf:li>bridge</rdf:li><rdf:li>hillside</rdf:li><rdf:li>passenger train</rdf:li><rdf:li>railroad</rdf:li><rdf:li>railroad bridge</rdf:li><rdf:li>span</rdf:li><rdf:li>train track</rdf:li><rdf:li>tree</rdf:li><rdf:li>viaduct</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|aqueduct</rdf:li><rdf:li>|arch</rdf:li><rdf:li>|arch bridge</rdf:li><rdf:li>|bridge</rdf:li><rdf:li>|hillside</rdf:li><rdf:li>|passenger train</rdf:li><rdf:li>|railroad</rdf:li><rdf:li>|railroad bridge</rdf:li><rdf:li>|span</rdf:li><rdf:li>|train track</rdf:li><rdf:li>|tree</rdf:li><rdf:li>|viaduct</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>aqueduct</rdf:li><rdf:li>arch</rdf:li><rdf:li>arch bridge</rdf:li><rdf:li>bridge</rdf:li><rdf:li>hillside</rdf:li><rdf:li>passenger train</rdf:li><rdf:li>railroad</rdf:li><rdf:li>railroad bridge</rdf:li><rdf:li>span</rdf:li><rdf:li>train track</rdf:li><rdf:li>tree</rdf:li><rdf:li>viaduct</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|aqueduct</rdf:li><rdf:li>st|arch</rdf:li><rdf:li>st|arch bridge</rdf:li><rdf:li>st|bridge</rdf:li><rdf:li>st|hillside</rdf:li><rdf:li>st|passenger train</rdf:li><rdf:li>st|railroad</rdf:li><rdf:li>st|railroad bridge</rdf:li><rdf:li>st|span</rdf:li><rdf:li>st|train track</rdf:li><rdf:li>st|tree</rdf:li><rdf:li>st|viaduct</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
@@ -2,6 +2,6 @@
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC03470.JPG">
|
||||
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>bus</rdf:li><rdf:li>illuminate</rdf:li><rdf:li>neon</rdf:li><rdf:li>neon light</rdf:li><rdf:li>night</rdf:li><rdf:li>sign</rdf:li><rdf:li>train car</rdf:li><rdf:li>trolley</rdf:li><rdf:li>window</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|bus</rdf:li><rdf:li>|illuminate</rdf:li><rdf:li>|neon</rdf:li><rdf:li>|neon light</rdf:li><rdf:li>|night</rdf:li><rdf:li>|sign</rdf:li><rdf:li>|train car</rdf:li><rdf:li>|trolley</rdf:li><rdf:li>|window</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>bus</rdf:li><rdf:li>illuminate</rdf:li><rdf:li>neon</rdf:li><rdf:li>neon light</rdf:li><rdf:li>night</rdf:li><rdf:li>sign</rdf:li><rdf:li>train car</rdf:li><rdf:li>trolley</rdf:li><rdf:li>window</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|bus</rdf:li><rdf:li>st|illuminate</rdf:li><rdf:li>st|neon</rdf:li><rdf:li>st|neon light</rdf:li><rdf:li>st|night</rdf:li><rdf:li>st|sign</rdf:li><rdf:li>st|train car</rdf:li><rdf:li>st|trolley</rdf:li><rdf:li>st|window</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
@@ -2,6 +2,6 @@
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC03508.ARW">
|
||||
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>building</rdf:li><rdf:li>ceiling</rdf:li><rdf:li>pillar</rdf:li><rdf:li>display</rdf:li><rdf:li>rail</rdf:li><rdf:li>steam engine</rdf:li><rdf:li>steam locomotive</rdf:li><rdf:li>train</rdf:li><rdf:li>train car</rdf:li><rdf:li>train track</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|building</rdf:li><rdf:li>|ceiling</rdf:li><rdf:li>|pillar</rdf:li><rdf:li>|display</rdf:li><rdf:li>|rail</rdf:li><rdf:li>|steam engine</rdf:li><rdf:li>|steam locomotive</rdf:li><rdf:li>|train</rdf:li><rdf:li>|train car</rdf:li><rdf:li>|train track</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>building</rdf:li><rdf:li>ceiling</rdf:li><rdf:li>pillar</rdf:li><rdf:li>display</rdf:li><rdf:li>rail</rdf:li><rdf:li>steam engine</rdf:li><rdf:li>steam locomotive</rdf:li><rdf:li>train</rdf:li><rdf:li>train car</rdf:li><rdf:li>train track</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|building</rdf:li><rdf:li>st|ceiling</rdf:li><rdf:li>st|pillar</rdf:li><rdf:li>st|display</rdf:li><rdf:li>st|rail</rdf:li><rdf:li>st|steam engine</rdf:li><rdf:li>st|steam locomotive</rdf:li><rdf:li>st|train</rdf:li><rdf:li>st|train car</rdf:li><rdf:li>st|train track</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
@@ -2,6 +2,6 @@
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC03508.JPG">
|
||||
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>attach</rdf:li><rdf:li>basement</rdf:li><rdf:li>beam</rdf:li><rdf:li>building</rdf:li><rdf:li>ceiling</rdf:li><rdf:li>equipment</rdf:li><rdf:li>floor</rdf:li><rdf:li>pipe</rdf:li><rdf:li>red</rdf:li><rdf:li>room</rdf:li><rdf:li>scaffold</rdf:li><rdf:li>tube</rdf:li><rdf:li>warehouse</rdf:li><rdf:li>water pipe</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|attach</rdf:li><rdf:li>|basement</rdf:li><rdf:li>|beam</rdf:li><rdf:li>|building</rdf:li><rdf:li>|ceiling</rdf:li><rdf:li>|equipment</rdf:li><rdf:li>|floor</rdf:li><rdf:li>|pipe</rdf:li><rdf:li>|red</rdf:li><rdf:li>|room</rdf:li><rdf:li>|scaffold</rdf:li><rdf:li>|tube</rdf:li><rdf:li>|warehouse</rdf:li><rdf:li>|water pipe</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>attach</rdf:li><rdf:li>basement</rdf:li><rdf:li>beam</rdf:li><rdf:li>building</rdf:li><rdf:li>ceiling</rdf:li><rdf:li>equipment</rdf:li><rdf:li>floor</rdf:li><rdf:li>pipe</rdf:li><rdf:li>red</rdf:li><rdf:li>room</rdf:li><rdf:li>scaffold</rdf:li><rdf:li>tube</rdf:li><rdf:li>warehouse</rdf:li><rdf:li>water pipe</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|attach</rdf:li><rdf:li>st|basement</rdf:li><rdf:li>st|beam</rdf:li><rdf:li>st|building</rdf:li><rdf:li>st|ceiling</rdf:li><rdf:li>st|equipment</rdf:li><rdf:li>st|floor</rdf:li><rdf:li>st|pipe</rdf:li><rdf:li>st|red</rdf:li><rdf:li>st|room</rdf:li><rdf:li>st|scaffold</rdf:li><rdf:li>st|tube</rdf:li><rdf:li>st|warehouse</rdf:li><rdf:li>st|water pipe</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
@@ -2,6 +2,6 @@
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="example.jpg">
|
||||
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>evening sky</rdf:li><rdf:li>sea</rdf:li><rdf:li>sky</rdf:li><rdf:li>storm cloud</rdf:li><rdf:li>stormy</rdf:li><rdf:li>sun</rdf:li><rdf:li>sunset</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|cloud</rdf:li><rdf:li>|cloudy</rdf:li><rdf:li>|evening sky</rdf:li><rdf:li>|sea</rdf:li><rdf:li>|sky</rdf:li><rdf:li>|storm cloud</rdf:li><rdf:li>|stormy</rdf:li><rdf:li>|sun</rdf:li><rdf:li>|sunset</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>evening sky</rdf:li><rdf:li>sea</rdf:li><rdf:li>sky</rdf:li><rdf:li>storm cloud</rdf:li><rdf:li>stormy</rdf:li><rdf:li>sun</rdf:li><rdf:li>sunset</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|cloud</rdf:li><rdf:li>st|cloudy</rdf:li><rdf:li>st|evening sky</rdf:li><rdf:li>st|sea</rdf:li><rdf:li>st|sky</rdf:li><rdf:li>st|storm cloud</rdf:li><rdf:li>st|stormy</rdf:li><rdf:li>st|sun</rdf:li><rdf:li>st|sunset</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
@@ -2,6 +2,6 @@
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="example.tif">
|
||||
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>evening sky</rdf:li><rdf:li>sea</rdf:li><rdf:li>sky</rdf:li><rdf:li>storm cloud</rdf:li><rdf:li>stormy</rdf:li><rdf:li>sun</rdf:li><rdf:li>sunset</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|cloud</rdf:li><rdf:li>|cloudy</rdf:li><rdf:li>|evening sky</rdf:li><rdf:li>|sea</rdf:li><rdf:li>|sky</rdf:li><rdf:li>|storm cloud</rdf:li><rdf:li>|stormy</rdf:li><rdf:li>|sun</rdf:li><rdf:li>|sunset</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>evening sky</rdf:li><rdf:li>sea</rdf:li><rdf:li>sky</rdf:li><rdf:li>storm cloud</rdf:li><rdf:li>stormy</rdf:li><rdf:li>sun</rdf:li><rdf:li>sunset</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|cloud</rdf:li><rdf:li>st|cloudy</rdf:li><rdf:li>st|evening sky</rdf:li><rdf:li>st|sea</rdf:li><rdf:li>st|sky</rdf:li><rdf:li>st|storm cloud</rdf:li><rdf:li>st|stormy</rdf:li><rdf:li>st|sun</rdf:li><rdf:li>st|sunset</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 3.5 MiB |
@@ -2,6 +2,6 @@
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC01106.jpg">
|
||||
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>clear</rdf:li><rdf:li>dark</rdf:li><rdf:li>moon</rdf:li><rdf:li>night</rdf:li><rdf:li>night sky</rdf:li><rdf:li>sky</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|clear</rdf:li><rdf:li>|dark</rdf:li><rdf:li>|moon</rdf:li><rdf:li>|night</rdf:li><rdf:li>|night sky</rdf:li><rdf:li>|sky</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>clear</rdf:li><rdf:li>dark</rdf:li><rdf:li>moon</rdf:li><rdf:li>night</rdf:li><rdf:li>night sky</rdf:li><rdf:li>sky</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|clear</rdf:li><rdf:li>st|dark</rdf:li><rdf:li>st|moon</rdf:li><rdf:li>st|night</rdf:li><rdf:li>st|night sky</rdf:li><rdf:li>st|sky</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
BIN
test/example/subfolder/subsubfolder/000041900001.jpg
Normal file
BIN
test/example/subfolder/subsubfolder/000041900001.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 MiB |
27
test/example/subfolder/subsubfolder/000041900001.jpg.xmp
Normal file
27
test/example/subfolder/subsubfolder/000041900001.jpg.xmp
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/"
|
||||
xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
|
||||
xmpMM:DerivedFrom="000041900001.jpg">
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>st</rdf:li>
|
||||
<rdf:li>cloud</rdf:li>
|
||||
<rdf:li>cloudy</rdf:li>
|
||||
<rdf:li>fly</rdf:li>
|
||||
<rdf:li>sky</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<lr:hierarchicalSubject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>st|cloud</rdf:li>
|
||||
<rdf:li>st|cloudy</rdf:li>
|
||||
<rdf:li>st|fly</rdf:li>
|
||||
<rdf:li>st|sky</rdf:li>
|
||||
</rdf:Bag>
|
||||
</lr:hierarchicalSubject>
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
BIN
test/example/subfolder/subsubfolder/000041900001.tif
Normal file
BIN
test/example/subfolder/subsubfolder/000041900001.tif
Normal file
Binary file not shown.
7
test/example/subfolder/subsubfolder/000041900001.tif.xmp
Normal file
7
test/example/subfolder/subsubfolder/000041900001.tif.xmp
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="000041900001.tif">
|
||||
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>fly</rdf:li><rdf:li>sky</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|cloud</rdf:li><rdf:li>st|cloudy</rdf:li><rdf:li>st|fly</rdf:li><rdf:li>st|sky</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
@@ -97,7 +97,7 @@ body {
|
||||
background-color: var(--color6);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color3);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ body {
|
||||
background-color: var(--color3);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color7);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
background-color: var(--bcolor1);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor3);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
background-color: var(--bcolor1);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor3);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
font-family: "Playfair Display", serif;
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color3);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
background-color: var(--bcolor2);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor4);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
font-family: "Nunito", sans-serif;
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color4);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
background-color: var(--bcolor2);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor4);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
background-color: var(--color3);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color4);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
background-color: var(--color2);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color4);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
background-color: var(--bcolor2);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor4);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
background-color: var(--color3);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color2);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
background-color: var(--bcolor2);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor3);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
background-color: var(--bcolor2);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor4);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
font-family: "Lora", serif;
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor3);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
background-color: var(--color4);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color3);
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor3);
|
||||
color: var(--bcolor2);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
background-color: var(--bcolor2);
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--bcolor4);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
font-family: "Montserrat", sans-serif;
|
||||
}
|
||||
|
||||
.tagentry:hover {
|
||||
.tagentry > label:hover {
|
||||
background-color: var(--color2);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user