major rework of configration parameters.

This commit is contained in:
2022-06-07 18:03:45 +03:00
parent afca67300e
commit f311b97d37
7 changed files with 295 additions and 120 deletions

View File

@@ -1,4 +1,4 @@
__version__ = "20220328.0"
__version__ = "20220607.0"
def get_version():

View File

@@ -4,8 +4,11 @@ import sys
import re
import shutil
import subprocess
from argparse import ArgumentParser
import random
from argparse import ArgumentParser, HelpFormatter
from mirva import get_version
from tqdm import tqdm
class Mirva:
@@ -22,19 +25,44 @@ class Mirva:
".*\.jpg$|.*\.jpeg$|.*\.png$|.*\.gif$|.*\.tif$", re.I
)
self.video_match = re.compile(".*\.mp4$", re.I)
self.site_defaults = {
"title": {"default": "", "help": "Title of the site"},
"sub_title": {
"default": "",
"help": "Subtitle of the site, shown under the title",
},
"intro": {
"default": "",
"help": "Intro text shown before first image",
},
"image_size": {
"default": "1920",
"help": "Resize images for faster loading. Use 'link' for symbolic links without resizing.",
},
"scroll": {
"default": "smooth",
"help": "Transition to next image with keyboard: smooth or auto",
},
}
## Init ##
self.get_options()
os.chdir(self.options.folder)
self.write_resources()
self.file_list = self.get_files()
if self.options.config or not os.path.exists(self.config_file):
self.create_config()
if self.run_commands["config"]:
self.write_resources()
updated = self.create_config()
if updated:
print(
"Config created: Exiting without gallery creation. Check config first."
"Config created or updated: Check config contents: {}".format(
self.config_file
)
return
)
if self.run_commands["generator"]:
self.get_config()
if self.options.exif:
self.append_exif()
self.create_posts()
self.write_index()
self.write_mediums()
@@ -46,12 +74,19 @@ class Mirva:
self.config.read(self.config_file)
config_changed = False
if not "SITE" in self.config:
self.config["SITE"] = {
"title": "",
"sub_title": "",
"intro": "",
"image_size": 850,
}
self.config["SITE"] = {}
config_changed = True
for key in self.site_defaults:
if not key in self.config["SITE"]:
config_changed = True
self.config["SITE"][key] = self.site_defaults[key]["default"]
if self.options.set:
for key, value in self.options.set:
if key not in self.site_defaults:
raise KeyError("Key '{}' is not a config keyword".format(key))
self.config["SITE"][key] = value
config_changed = True
for f in self.file_list:
@@ -60,9 +95,27 @@ class Mirva:
title = title.replace("_", " ")
self.config[f] = {"title": title, "description": ""}
config_changed = True
print("Added {}".format(f))
if self.options.purge:
unnecessary = []
for f in self.config:
if f in ("SITE", "DEFAULT"):
continue
if f not in self.file_list:
print("{} not found in files".format(f))
unnecessary.append(f)
for f in unnecessary:
del self.config[f]
config_changed = True
if self.options.exif:
self.append_exif()
config_changed = True
if config_changed:
self.write_config()
return config_changed
def create_posts(self):
@@ -93,7 +146,6 @@ class Mirva:
self.config.write(fp)
def get_files(self):
image_match = re.compile(".*\.jpg$|.*\.jpeg$|.*\.png$|.*\.gif$|.*\.tif$", re.I)
files = []
for f in sorted(os.listdir(".")):
if f.startswith("."):
@@ -104,24 +156,24 @@ class Mirva:
return files
def get_options(self):
parser = ArgumentParser(
prog="mirva",
epilog='Configuration note: item "image_size = [integer]" is the '
+ "middle sized image max width/height in pixels. It also accepts a special "
+ 'value "link" to make symbolic links.',
)
# parser.add_argument("-v", default=False, action="store_true")
parser = ArgumentParser(prog="mirva", formatter_class=SmartFormatter)
parser.add_argument(
"--config",
default=False,
action="store_true",
help="Write config and exit. Required if more images are added.",
"--version",
action="version",
version="%(prog)s {version}".format(version=get_version()),
)
parser.add_argument(
"--force",
"--folder",
type=str,
default=".",
help="Folder for gallery. Default current folder.",
)
parser.add_argument(
"--purge",
default=False,
action="store_true",
help="Force regeneration of middle sized images",
help="Remove non-existent files in image list. Required if images are removed.",
)
parser.add_argument(
"--exif",
@@ -130,20 +182,43 @@ class Mirva:
help="Append EXIF information to image descriptions",
)
parser.add_argument(
"--version",
action="version",
version="%(prog)s {version}".format(version=get_version()),
"--set",
"-s",
default=None,
nargs=2,
action="append",
metavar=("key", "value"),
help="smart|Configurable options: \n"
+ "\n".join(
["{}: {}".format(k, v["help"]) for k, v in self.site_defaults.items()]
),
)
parser.add_argument(
"--force",
default=False,
action="store_true",
help="Force regeneration of middle sized images",
)
parser.add_argument(
"folder",
type=str,
default=".",
nargs="?",
help="Folder for gallery",
"--command",
"-c",
default="all",
action="store",
choices=["all", "config", "generator"],
help="Run only part of the process. Defaults to all. Config is run always if it doesn't exist.",
)
self.options = parser.parse_args()
def get_index(self, page_title, sub_title, intro, posts):
self.options = parser.parse_args()
self.run_commands = {
"config": self.options.command == "config"
or self.options.command == "all"
or not os.path.exists(self.config_file),
"generator": self.options.command == "generator"
or self.options.command == "all",
}
def get_index(self, posts):
return """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
@@ -164,10 +239,10 @@ Released : 20110306
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" href="{resource}/mirva.ico"/>
<title>{page_title}</title>
<link href="{resource}/style.css" rel="stylesheet" type="text/css" media="screen" />
<link href="{resource}/mirva.css" rel="stylesheet" type="text/css" media="screen" />
<script src="{resource}/mirva.js"></script>
</head>
<body>
<body data-scroll="{scroll}">
<div id="wrapper">
<div id="header">
<div id="logo">
@@ -183,7 +258,7 @@ Released : 20110306
<div id="page-bgbtm">
<div id="content">
<div class="post">
<div class="entry">
<div class="entry intro">
{intro}
</div>
</div>
@@ -199,9 +274,10 @@ Released : 20110306
</div>
</body>
</html>""".format(
page_title=page_title,
sub_title=sub_title,
intro=intro,
page_title=self.config["SITE"]["title"],
sub_title=self.config["SITE"]["sub_title"],
intro=self.config["SITE"]["intro"],
scroll=self.config["SITE"]["scroll"],
posts="\n".join(posts),
resource=self.resource_dir,
)
@@ -212,7 +288,7 @@ Released : 20110306
return """
<div class="post">
<div class="navigation">&nbsp;</div>
<div class=center><a href="{image}">
<div class="image_wrapper center"><a href="{image}">
<video class=post_image controls preload="metadata">
<source src="{image}" type="video/mp4" >
</video>
@@ -227,7 +303,9 @@ Released : 20110306
return """
<div class="post">
<div class="navigation">&nbsp;</div>
<div class=center><a href="{image}"><img loading=lazy class=post_image src="{med_dir}/{image}.jpg"></a></div>
<div class="image_wrapper center"><a href="{image}">
<img loading=lazy class=post_image src="{med_dir}/{image}.jpg">
</a></div>
<div class="meta"><div class="name">{title}</div></div>
<div style="clear: both;">&nbsp;</div>
<div class="entry">{content}</div>
@@ -253,14 +331,7 @@ Released : 20110306
sys.exit(1)
with open("index.html", "wt") as fp:
fp.write(
self.get_index(
self.config["SITE"]["title"],
self.config["SITE"]["sub_title"],
self.config["SITE"]["intro"],
self.posts,
)
)
fp.write(self.get_index(self.posts))
def write_resources(self):
@@ -270,17 +341,45 @@ Released : 20110306
pass
for f in (
"style.css",
"mirva.css",
"arrow_up.png",
"arrow_down.png",
"banner.jpg",
"mirva.ico",
"mirva.js",
):
if os.path.exists(os.path.join(self.resource_dir, f)):
continue
shutil.copy(
os.path.join(self.resource_src, f), os.path.join(self.resource_dir, f)
os.path.join(self.resource_src, f),
os.path.join(self.resource_dir, f),
)
if not os.path.exists(os.path.join(self.resource_dir, "banner.jpg")):
try:
f = random.choice(self.file_list)
outfile = os.path.join(self.resource_dir, "banner.jpg")
res = "1920x"
convargs = [
"convert",
"{}[0]".format(f),
"-background",
"white",
"-flatten",
"-resize",
res,
"+contrast",
"+contrast",
"+contrast",
"-quality",
"85",
outfile,
]
subprocess.run(convargs)
print("Random banner written to {}".format(outfile))
except Exception:
shutil.copy(
os.path.join(self.resource_src, "banner.jpg"),
os.path.join(self.resource_dir, "banner.jpg"),
)
def write_mediums(self):
@@ -290,14 +389,14 @@ Released : 20110306
except Exception:
pass
r = self.config["SITE"].get("image_size", 850)
r = self.config["SITE"].get("image_size", 1920)
link = str(r).lower() == "link"
if link:
r = 0
res = "{:d}x{:d}>".format(int(r), int(r))
force = self.options.force
for f in self.file_list:
for f in tqdm(self.file_list):
if self.video_match.match(f):
continue
outfile = os.path.join(self.medium_dir, "{}.jpg".format(f))
@@ -309,14 +408,11 @@ Released : 20110306
if not os.path.exists(outfile):
if link:
# TODO: Get absolute path to f?
os.symlink("../../{}".format(f), outfile)
continue
convargs = [
"convert",
"-define",
"jpeg:size={}x{}".format(r, r),
"{}[0]".format(f),
"-background",
"white",
@@ -327,8 +423,6 @@ Released : 20110306
"85",
outfile,
]
sys.stdout.write(".")
sys.stdout.flush()
subprocess.run(convargs)
sys.stdout.write("\n")
@@ -359,7 +453,6 @@ Released : 20110306
self.config[f]["description"] += p.stdout.decode("utf-8")
sys.stdout.write("\n")
self.write_config()
def human_size(file_name, precision=1):
@@ -379,3 +472,10 @@ def human_size(file_name, precision=1):
size = size / 1024.0
defPrecision = precision
return "%s%.*f%s" % (sign, defPrecision, size, suffixes[suffixIndex])
class SmartFormatter(HelpFormatter):
def _split_lines(self, help, width):
if help.startswith("smart|"):
return help[6:].splitlines()
return HelpFormatter._split_lines(self, help, width)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -64,7 +64,7 @@ a:hover {
height: 330px;
margin: 0 auto;
padding: 0px;
background: url(banner.jpg) no-repeat left top;
background: url(banner.jpg) no-repeat center center;
background-size: cover;
}

View File

@@ -1,11 +1,16 @@
let current = -1;
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
let currentScrolled = -1;
let scrollTimer = null;
let scrollEventTimer = null;
function r(f) {
/in/.test(document.readyState) ? setTimeout('r(' + f + ')', 9) : f()
}
r(function() {
create_nav();
document.onkeydown = keyboard_entry;
document.onwheel = scroll_event;
});
function create_nav() {
let navis = document.getElementsByClassName("navigation");
for (let i = 0; i < navis.length; i++) {
@@ -23,6 +28,7 @@ function create_nav() {
}
}
}
function create_button(direction, to, next) {
let container = document.createElement('div');
container.classList.add("float_" + direction);
@@ -30,25 +36,79 @@ function create_button(direction, to, next) {
button.src = ".mirva/arrow_" + direction + ".png";
button.classList.add("navigation_button");
button.onclick = function() {
to.parentElement.scrollIntoView({behavior: "smooth"});
to.parentElement.scrollIntoView({
behavior: "smooth"
});
current = next;
};
container.appendChild(button);
return container
}
function is_visible(el) {
function is_visible(el, type) {
let rect = el.getBoundingClientRect();
if (type == "fully") {
return rect.top >= 0 && rect.top < window.innerHeight && rect.bottom >= 0 && rect.bottom <= window.innerHeight
} else {
return rect.top < window.innerHeight && rect.bottom >= 0;
}
function scroll_event(ev) {
let navis = document.getElementsByClassName("navigation");
for (let i = 0; i<navis.length; i++) {
if (is_visible(navis[i])) {
current = i -1;
return
}
function get_position() {
let intro = document.getElementsByClassName("intro");
for (let i = 0; i < intro.length; i++) {
if (is_visible(intro[i], "fully")) {
return {
"current": -1,
"next": 0,
"previous": -1,
"type": "top"
}
}
}
let posts = document.getElementsByClassName("image_wrapper");
for (let i = 0; i < posts.length; i++) {
if (is_visible(posts[i], "fully")) {
return {
"current": i,
"next": i + 1,
"previous": i - 1,
"type": "full"
}
}
}
let partials = [];
for (let i = 0; i < posts.length; i++) {
if (is_visible(posts[i], "partial")) {
partials.push(i)
}
}
if (partials.length == 1) {
return {
"current": partials[0],
"next": partials[0] + 1,
"previous": partials[0] - 1,
"type": "partial single"
}
}
if (partials.length > 1) {
return {
"current": partials[0],
"next": partials[0] + 1,
"previous": partials[0],
"type": "partial multi"
}
}
return {
"current": current,
"next": current + 1,
"previous": current - 1,
"type": "unknown"
}
}
function keyboard_entry(ev) {
let kC = ev.keyCode;
let ctrlDown = ev.ctrlKey || ev.metaKey;
@@ -58,25 +118,39 @@ function keyboard_entry(ev) {
if ([32, 33, 34, 35, 36, 37, 38, 39, 40].indexOf(kC) == -1) {
return
}
let navis = document.getElementsByClassName("navigation");
let posts = document.getElementsByClassName("image_wrapper");
let position = get_position();
if (kC == '36') { // home
current = -1;
return
}
else if (kC == '35') { // end
current = navis.length;
} else if (kC == '35') { // end
return
}
else if (kC == '39') { // right
current += 1;
}
else if (kC == '37') { // left
current -= 1;
} else if (kC == '39') { // right
current = position.next;
} else if (kC == '37') { // left
current = position.previous;
} else {
setTimeout(scroll_event, 100);
return
}
if (current == -1) {
window.scrollTo({ top: 0, behavior: 'smooth' });
return
}
current = Math.max(0, current);
current = Math.min(navis.length-1, current);
navis[current].parentElement.scrollIntoView({behavior: "smooth"});
current = Math.min(posts.length - 1, current);
/* Scroll smooth, if no frequent keypress */
if (scrollTimer) {
posts[current].parentElement.scrollIntoView({
behavior: "auto"
});
clearTimeout(scrollTimer);
} else {
posts[current].parentElement.scrollIntoView({
behavior: "smooth"
});
}
scrollTimer = setTimeout(function() {
scrollTimer = null;
},
200
);
}

View File

@@ -11,4 +11,5 @@ setup(
"mirva=mirva:main",
],
},
install_requires=["tqdm"],
)