first version
This commit is contained in:
255
mirva/mirva.py
Executable file
255
mirva/mirva.py
Executable file
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import configparser
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from argparse import ArgumentParser
|
||||
|
||||
|
||||
class Mirva:
|
||||
def __init__(self):
|
||||
|
||||
self.resource_src = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), "resources"
|
||||
)
|
||||
self.resource_dir = ".template"
|
||||
self.config_file = os.path.join(self.resource_dir, "config")
|
||||
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()
|
||||
print("Exiting. Check config.")
|
||||
return
|
||||
self.get_config()
|
||||
self.create_posts()
|
||||
self.write_index()
|
||||
self.write_mediums()
|
||||
|
||||
def create_config(self):
|
||||
print("Creating config: {}".format(os.path.join(self.options.folder,self.config_file)))
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(self.config_file)
|
||||
if not "SITE" in config:
|
||||
config["SITE"] = {
|
||||
"title": "",
|
||||
"sub_title": "",
|
||||
"intro": "",
|
||||
}
|
||||
|
||||
for f in self.file_list:
|
||||
if not f in config:
|
||||
config[f] = {"title": f, "description": ""}
|
||||
|
||||
with open(self.config_file, "wt") as fp:
|
||||
config.write(fp)
|
||||
|
||||
def create_posts(self):
|
||||
|
||||
self.posts = []
|
||||
for f in self.file_list:
|
||||
if not f in self.config:
|
||||
post = self.get_post(f, f, "")
|
||||
else:
|
||||
post = self.get_post(
|
||||
f, self.config[f]["title"], self.config[f]["description"]
|
||||
)
|
||||
self.posts.append(post)
|
||||
|
||||
def get_config(self):
|
||||
self.config = configparser.ConfigParser()
|
||||
self.config.read(self.config_file)
|
||||
|
||||
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("."):
|
||||
continue
|
||||
if not image_match.match(f):
|
||||
continue
|
||||
files.append(f)
|
||||
return files
|
||||
|
||||
def get_options(self):
|
||||
parser = ArgumentParser()
|
||||
#parser.add_argument("-v", default=False, action="store_true")
|
||||
parser.add_argument(
|
||||
"--config", default=False, action="store_true", help="Write config and exit"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"folder",
|
||||
type=str,
|
||||
default=".",
|
||||
nargs="?",
|
||||
help="Folder for gallery",
|
||||
)
|
||||
self.options = parser.parse_args()
|
||||
|
||||
def get_index(self, page_title, sub_title, intro, posts):
|
||||
|
||||
return """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!--
|
||||
Design by TEMPLATED
|
||||
http://templated.co
|
||||
Released for free under the Creative Commons Attribution License
|
||||
|
||||
Name : Green Forest
|
||||
Description: A two-column, fixed-width design with dark color scheme.
|
||||
Version : 1.0
|
||||
Released : 20110306
|
||||
|
||||
-->
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="keywords" content="" />
|
||||
<meta name="description" content="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<title>{page_title}</title>
|
||||
<link href="{resource}/style.css" rel="stylesheet" type="text/css" media="screen" />
|
||||
<script>
|
||||
function r(f){{/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}}
|
||||
r(function(){{
|
||||
create_nav();
|
||||
}});
|
||||
function create_nav() {{
|
||||
let navis = document.getElementsByClassName("navigation");
|
||||
for (let i = 0; i<navis.length; i++) {{
|
||||
if (navis[i-1]) {{
|
||||
navis[i].appendChild(create_button("up", navis[i-1]));
|
||||
}} else {{
|
||||
navis[i].appendChild(create_button("up", document.body));
|
||||
}}
|
||||
if (navis[i+1]) {{
|
||||
navis[i].appendChild(create_button("down", navis[i+1]));
|
||||
}}
|
||||
|
||||
}}
|
||||
}}
|
||||
function create_button(direction, to) {{
|
||||
let container = document.createElement('div');
|
||||
container.classList.add("float_" + direction);
|
||||
let button = document.createElement('img');
|
||||
button.src = "{resource}/arrow_" + direction + ".png";
|
||||
button.classList.add("navigation_button");
|
||||
button.onclick = function(){{
|
||||
to.parentElement.scrollIntoView({{behavior: "smooth"}});
|
||||
}};
|
||||
container.appendChild(button);
|
||||
return container
|
||||
}}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<div id="header">
|
||||
<div id="logo">
|
||||
<h1 id="page_title">{page_title}</h1>
|
||||
<p>{sub_title}</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end #header -->
|
||||
<div id="page">
|
||||
<div id="page-bgtop">
|
||||
<div id="page-bgbtm">
|
||||
<div id="content">
|
||||
<div class="post">
|
||||
<div class="entry">
|
||||
{intro}
|
||||
</div>
|
||||
</div>
|
||||
{posts}
|
||||
|
||||
<div style="clear: both;"> </div>
|
||||
</div>
|
||||
<!-- end #content -->
|
||||
<div style="clear: both;"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end #page -->
|
||||
</div>
|
||||
</body>
|
||||
</html>""".format(
|
||||
page_title=page_title,
|
||||
sub_title=sub_title,
|
||||
intro=intro,
|
||||
posts="\n".join(posts),
|
||||
resource=self.resource_dir,
|
||||
)
|
||||
|
||||
def get_post(self, image, title, content):
|
||||
|
||||
return """
|
||||
<div class="post">
|
||||
<div class="navigation"></div>
|
||||
<div class=center><a href="{image}"><img class=post_image src=".med/{image}.jpg"></a></div>
|
||||
<p class="meta"><span class="date">{title}</span></p>
|
||||
<div style="clear: both;"> </div>
|
||||
<div class="entry">{content}</div>
|
||||
</div>""".format(
|
||||
image=image, title=title, content=content
|
||||
)
|
||||
|
||||
def write_index(self):
|
||||
|
||||
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,
|
||||
)
|
||||
)
|
||||
|
||||
def write_resources(self):
|
||||
|
||||
try:
|
||||
os.makedirs(self.resource_dir)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for f in ("style.css", "arrow_up.png", "arrow_down.png", "banner.jpg"):
|
||||
shutil.copy(
|
||||
os.path.join(self.resource_src, f), os.path.join(self.resource_dir, f)
|
||||
)
|
||||
|
||||
def write_mediums(self):
|
||||
|
||||
try:
|
||||
os.makedirs(".med")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# TODO: config image res
|
||||
# TODO opts: force recreation
|
||||
r = 850
|
||||
force = False
|
||||
for f in self.file_list:
|
||||
res = "{:d}x{:d}>".format(r, r)
|
||||
outfile = os.path.join(".med", "{}.jpg".format(f))
|
||||
if not os.path.exists(outfile) or force:
|
||||
convargs = [
|
||||
"convert",
|
||||
"-define",
|
||||
"jpeg:size={}x{}".format(r, r),
|
||||
"{}[0]".format(f),
|
||||
"-background",
|
||||
"white",
|
||||
"-flatten",
|
||||
"-resize",
|
||||
res,
|
||||
"-quality",
|
||||
"85",
|
||||
outfile,
|
||||
]
|
||||
subprocess.call(convargs)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user