move to json config. allow subfolders

This commit is contained in:
Q
2023-10-13 23:52:46 +03:00
parent 7f0412a9e4
commit 076279c261
3 changed files with 165 additions and 57 deletions

View File

@@ -1,4 +1,4 @@
__version__ = "20230929.0" __version__ = "20231013.0"
def get_version(): def get_version():

View File

@@ -1,10 +1,11 @@
import configparser import configparser
import json
import os import os
import sys import random
import re import re
import shutil import shutil
import subprocess import subprocess
import random import sys
import urllib.parse import urllib.parse
from argparse import ArgumentParser, HelpFormatter from argparse import ArgumentParser, HelpFormatter
@@ -14,16 +15,13 @@ from tqdm import tqdm
class Mirva: class Mirva:
def __init__(self): def __init__(self):
self.resource_src = os.path.join( self.resource_src = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources")
os.path.dirname(os.path.abspath(__file__)), "resources"
)
self.resource_dir = ".mirva" self.resource_dir = ".mirva"
self.medium_dir = os.path.join(self.resource_dir, "med") self.medium_dir = os.path.join(self.resource_dir, "med")
self.config_file = os.path.join(self.resource_dir, "config.cfg") self.config_file = os.path.join(self.resource_dir, "config.json")
self.config_backup = os.path.join(self.resource_dir, "config.cfg.bkp") self.config_file_old = os.path.join(self.resource_dir, "config.cfg")
self.image_match = re.compile( self.config_backup = os.path.join(self.resource_dir, "config.bkp")
".*\.jpg$|.*\.jpeg$|.*\.png$|.*\.gif$|.*\.tif$", re.I self.image_match = re.compile(".*\.jpg$|.*\.jpeg$|.*\.png$|.*\.gif$|.*\.tif$", re.I)
)
self.video_match = re.compile(".*\.mp4$", re.I) self.video_match = re.compile(".*\.mp4$", re.I)
self.site_defaults = { self.site_defaults = {
"title": {"default": "", "help": "Title of the site"}, "title": {"default": "", "help": "Title of the site"},
@@ -49,28 +47,27 @@ class Mirva:
self.get_options() self.get_options()
os.chdir(self.options.folder) os.chdir(self.options.folder)
self.file_list = self.get_files() self.file_list = self.get_files()
self.folder_list = self.get_folders()
if self.run_commands["config"]: if self.run_commands["config"]:
self.write_resources() self.write_resources()
updated = self.create_config() updated = self.create_config()
if updated: if updated:
print( print("Config created or updated: Check config contents: {}".format(self.config_file))
"Config created or updated: Check config contents: {}".format(
self.config_file
)
)
if self.run_commands["build"]: if self.run_commands["build"]:
self.get_config() self.get_config()
self.create_posts() self.create_posts()
self.create_folders()
self.write_index() self.write_index()
self.write_mediums() self.write_mediums()
print("Gallery written.") print("Gallery written.")
def create_config(self): def create_config(self):
self.config = configparser.RawConfigParser() self.get_config()
self.config.read(self.config_file)
config_changed = False config_changed = False
## SITE
if not "SITE" in self.config: if not "SITE" in self.config:
self.config["SITE"] = {} self.config["SITE"] = {}
config_changed = True config_changed = True
@@ -87,25 +84,55 @@ class Mirva:
self.config["SITE"][key] = value self.config["SITE"][key] = value
config_changed = True config_changed = True
## FOLDERS
if not "FOLDERS" in self.config:
self.config["FOLDERS"] = []
config_changed = True
path_list = [i["path"] for i in self.config["FOLDERS"]]
if self.options.add_back:
if not ".." in path_list:
self.config["FOLDERS"].insert(0, {"path": "..", "title": "Back", "thumb": '[guess]'})
config_changed = True
for d in self.folder_list:
if not d in path_list:
self.config["FOLDERS"].append({"path": d, "title": d, "thumb": '[guess]'})
config_changed = True
## IMAGES
if not "IMAGES" in self.config:
self.config["IMAGES"] = []
config_changed = True
path_list = [i["path"] for i in self.config["IMAGES"]]
for f in self.file_list: for f in self.file_list:
if not f in self.config: if not f in path_list:
title, _ = os.path.splitext(f) title, _ = os.path.splitext(f)
title = title.replace("_", " ") title = title.replace("_", " ")
self.config[f] = {"title": title, "description": ""} self.config["IMAGES"].append({"path": f, "title": title, "description": ""})
config_changed = True config_changed = True
print("Added {}".format(f)) print("Added {}".format(f))
if self.options.purge: if self.options.purge:
unnecessary = [] unnecessary_files = []
for f in self.config: unnecessary_folders = []
if f in ("SITE", "DEFAULT"): for i, f in enumerate(self.config["IMAGES"]):
if f["path"] not in self.file_list:
print("{} not found in files".format(f["path"]))
unnecessary_files.append(i)
for i, d in enumerate(self.config["FOLDERS"]):
if d["path"] == "..":
continue continue
if f not in self.file_list: if d["path"] not in self.folder_list:
print("{} not found in files".format(f)) unnecessary_folders.append(i)
unnecessary.append(f) if len(unnecessary_files) > 0:
for f in unnecessary:
del self.config[f]
config_changed = True config_changed = True
if len(unnecessary_folders) > 0:
config_changed = True
self.config["IMAGES"] = [d for i, d in enumerate(self.config["IMAGES"]) if i not in unnecessary_files]
self.config["FOLDERS"] = [d for i, d in enumerate(self.config["FOLDERS"]) if i not in unnecessary_folders]
if self.options.exif: if self.options.exif:
self.append_exif() self.append_exif()
@@ -117,41 +144,75 @@ class Mirva:
def create_posts(self): def create_posts(self):
self.posts = [] self.posts = []
for c in self.config: for c in self.config["IMAGES"]:
if c in self.file_list: if c["path"] in self.file_list:
post = self.get_post( post = self.get_post(c["path"], c["title"], c["description"])
c, self.config[c]["title"], self.config[c]["description"]
)
self.posts.append(post) self.posts.append(post)
def create_folders(self):
self.folders = []
for c in self.config["FOLDERS"]:
self.folders.append(self.get_folder(c["path"], c["title"], c["thumb"]))
def get_config(self): def get_config(self):
self.config = configparser.RawConfigParser() if os.path.exists(self.config_file_old):
self.config.read(self.config_file) # Migration from old style config
self.config_old = configparser.RawConfigParser()
self.config_old.read(self.config_file_old)
self.config = {}
self.config["SITE"] = dict(self.config_old.items("SITE"))
self.config["IMAGES"] = []
for f in self.config_old.sections():
if f in ("IMAGES", "SITE"):
continue
self.config["IMAGES"].append(
{
"path": f,
"title": self.config_old[f]["title"],
"description": self.config_old[f]["description"],
}
)
os.remove(self.config_file_old)
return
if os.path.exists(self.config_file):
with open(self.config_file, "rt") as fp:
self.config = json.load(fp)
return
self.config = {}
def write_config(self): def write_config(self):
print( print("Modified config: {}".format(os.path.join(self.options.folder, self.config_file)))
"Modified config: {}".format(
os.path.join(self.options.folder, self.config_file)
)
)
if os.path.exists(self.config_file): if os.path.exists(self.config_file):
with open(self.config_file, "rt") as reader: with open(self.config_file, "rt") as reader:
with open(self.config_backup, "wt") as writer: with open(self.config_backup, "wt") as writer:
writer.write(reader.read()) writer.write(reader.read())
with open(self.config_file, "wt") as fp: with open(self.config_file, "wt") as fp:
self.config.write(fp) json.dump(self.config, fp, indent=2)
def get_files(self): def get_files(self):
files = [] files = []
for f in sorted(os.listdir(".")): for f in sorted(os.listdir(".")):
if f.startswith("."): if f.startswith("."):
continue continue
if not os.path.isfile(f):
continue
if not (self.image_match.match(f) or self.video_match.match(f)): if not (self.image_match.match(f) or self.video_match.match(f)):
continue continue
files.append(f) files.append(f)
return files return files
def get_folders(self):
folders = []
for f in sorted(os.listdir(".")):
if f.startswith("."):
continue
if not os.path.isdir(f):
continue
folders.append(f)
return folders
def get_options(self): def get_options(self):
parser = ArgumentParser(prog="mirva", formatter_class=SmartFormatter) parser = ArgumentParser(prog="mirva", formatter_class=SmartFormatter)
parser.add_argument( parser.add_argument(
@@ -165,7 +226,12 @@ class Mirva:
default=".", default=".",
help="Folder for gallery. Default current folder.", help="Folder for gallery. Default current folder.",
) )
parser.add_argument(
"--add-back",
default=False,
action="store_true",
help="Add 'Back' link to parent folder.",
)
parser.add_argument( parser.add_argument(
"--purge", "--purge",
default=False, default=False,
@@ -186,9 +252,7 @@ class Mirva:
action="append", action="append",
metavar=("key", "value"), metavar=("key", "value"),
help="smart|Configurable options: \n" help="smart|Configurable options: \n"
+ "\n".join( + "\n".join(["{}: {}".format(k, v["help"]) for k, v in self.site_defaults.items()]),
["{}: {}".format(k, v["help"]) for k, v in self.site_defaults.items()]
),
) )
parser.add_argument( parser.add_argument(
@@ -214,7 +278,7 @@ class Mirva:
"build": self.options.command == "build" or self.options.command == "all", "build": self.options.command == "build" or self.options.command == "all",
} }
def get_index(self, posts): def get_index(self):
return """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> return """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- <!--
Design by TEMPLATED Design by TEMPLATED
@@ -252,6 +316,11 @@ Released : 20110306
<div id="page-bgtop"> <div id="page-bgtop">
<div id="page-bgbtm"> <div id="page-bgbtm">
<div id="content"> <div id="content">
<div id="post_folders">
<div class="entry folders">
{folders}
</div>
</div>
<div class="post" id="post_intro"> <div class="post" id="post_intro">
<div class="entry intro"> <div class="entry intro">
{intro} {intro}
@@ -273,7 +342,8 @@ Released : 20110306
sub_title=self.config["SITE"]["sub_title"], sub_title=self.config["SITE"]["sub_title"],
intro=self.config["SITE"]["intro"], intro=self.config["SITE"]["intro"],
scroll=self.config["SITE"]["scroll"], scroll=self.config["SITE"]["scroll"],
posts="\n".join(posts), folders="\n".join(self.folders),
posts="\n".join(self.posts),
resource=self.resource_dir, resource=self.resource_dir,
) )
@@ -307,6 +377,32 @@ Released : 20110306
</div>""" </div>"""
).format(image=image, title=title, content=content, med_dir=self.medium_dir) ).format(image=image, title=title, content=content, med_dir=self.medium_dir)
def get_folder(self, path, title, thumb):
if thumb == '[guess]':
try:
img = (
"style=\"background-image: url('"
+ urllib.parse.quote(
os.path.join(
path,
self.resource_dir,
"med",
sorted(
[f for f in os.listdir(os.path.join(path, self.resource_dir, "med")) if f.endswith(".jpg")]
)[0],
)
)
+ "')\""
)
except Exception:
img = ""
else:
img = "style=\"background-image: url('{}')\"".format(thumb)
return '<div class=folder_wrapper><a href="{path}" title="{title}"><span class="folder_thumb" {img}></span>{title}</a></div>'.format(
path=urllib.parse.quote(path), title=title, img=img
)
def is_created_with_mirva(self): def is_created_with_mirva(self):
with open("index.html", "rt") as fp: with open("index.html", "rt") as fp:
for line in fp.readlines(): for line in fp.readlines():
@@ -317,13 +413,11 @@ Released : 20110306
def write_index(self): def write_index(self):
if os.path.exists("index.html"): if os.path.exists("index.html"):
if not self.is_created_with_mirva(): if not self.is_created_with_mirva():
print( print("index.html exists, and it's not written with Mirva. Not overwriting.")
"index.html exists, and it's not written with Mirva. Not overwriting."
)
sys.exit(1) sys.exit(1)
with open("index.html", "wt") as fp: with open("index.html", "wt") as fp:
fp.write(self.get_index(self.posts)) fp.write(self.get_index())
def write_resources(self): def write_resources(self):
try: try:
@@ -427,22 +521,21 @@ Released : 20110306
</ul> </ul>
""" """
for f in self.config: for f in self.config["IMAGES"]:
if f in self.file_list: if f["path"] in self.file_list:
sys.stdout.write(".") sys.stdout.write(".")
sys.stdout.flush() sys.stdout.flush()
file_size = human_size(f) file_size = human_size(f["path"])
p = subprocess.run( p = subprocess.run(
[ [
"identify", "identify",
"-format", "-format",
exif_format.format(size=file_size), exif_format.format(size=file_size),
"{}[0]".format(f), "{}[0]".format(f["path"]),
], ],
capture_output=True, capture_output=True,
) )
f["description"] += p.stdout.decode("utf-8")
self.config[f]["description"] += p.stdout.decode("utf-8")
sys.stdout.write("\n") sys.stdout.write("\n")

View File

@@ -175,6 +175,21 @@ a:hover {}
.post-bgbtm {} .post-bgbtm {}
.folder_wrapper {
margin-top: 3px;
font-size: 15px;
}
.folders .folder_thumb {
width: 150px;
height: 40px;
background-size: cover;
vertical-align: middle;
margin-right: 2em;
display: inline-block;
background-position: center;
}
.post .title { .post .title {
height: 38px; height: 38px;
margin-bottom: 10px; margin-bottom: 10px;