move to json config. allow subfolders
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
__version__ = "20230929.0"
|
__version__ = "20231013.0"
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
|
|||||||
205
mirva/mirva.py
205
mirva/mirva.py
@@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user