refactor image code to a BlogImage class, test markdown_template functionality
This commit is contained in:
parent
65316276dc
commit
15d38a5ceb
129
backblogger.py
129
backblogger.py
|
@ -3,28 +3,91 @@ import os
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
import cv2
|
import cv2
|
||||||
|
from PIL import Image as PILImage
|
||||||
|
from PIL.ExifTags import TAGS
|
||||||
|
|
||||||
def markdown_date(ts):
|
def markdown_date(ts):
|
||||||
d = datetime.fromtimestamp(ts)
|
d = datetime.fromtimestamp(ts)
|
||||||
return f"{d.year}-{d.month}-{d.day}"
|
return f"{d.year}-{d.month}-{d.day}"
|
||||||
|
|
||||||
IMG_WIDTH = 760
|
IMG_WIDTH = 760
|
||||||
def resize_rename(imgpath, article_number, image_number, destpath=None, target_width=IMG_WIDTH):
|
class BlogImage:
|
||||||
new_fn = f"article{str(article_number).zfill(2)}_image{str(image_number).zfill(2)}.{imgpath.split('.')[-1]}"
|
def __init__(self, filepath, article_number=None, image_number=None):
|
||||||
if destpath is None:
|
self.path = filepath
|
||||||
destpath = os.path.join('.', imgpath.replace(os.path.basename(imgpath), ''))
|
self.article_number = article_number
|
||||||
dest_fn = os.path.join(destpath, new_fn)
|
self.image_number = image_number
|
||||||
img = cv2.imread(imgpath)
|
self._metadata = None
|
||||||
h, w = img.shape[:2]
|
self.resized = False
|
||||||
print(h, w)
|
self.file_date = os.stat(self.path).st_ctime if self.path else None
|
||||||
if w <= target_width:
|
|
||||||
cv2.imwrite(dest_fn, img)
|
def metadata(self, force_reload=False):
|
||||||
return
|
if self._metadata and not force_reload: return self._metadata
|
||||||
ratio = target_width / float(w)
|
img = PILImage.open(self.path)
|
||||||
new_h = int(h * ratio)
|
exif = img.getexif()
|
||||||
print(ratio, target_width, new_h)
|
self._metadata = {TAGS.get(t, t): exif[t] for t in exif}
|
||||||
new_img = cv2.resize(img, (target_width, new_h), interpolation=cv2.INTER_AREA)
|
return self._metadata
|
||||||
cv2.imwrite(dest_fn, new_img)
|
|
||||||
|
def blog_image_name(self):
|
||||||
|
AN = str(self.article_number).zfill(2)
|
||||||
|
IN = str(self.image_number).zfill(2)
|
||||||
|
EXT = os.path.basename(self.path).split('.')[1]
|
||||||
|
return f"article{AN}_image{IN}.{EXT}"
|
||||||
|
|
||||||
|
def resize(self, target_width, dest_fn=None):
|
||||||
|
if dest_fn is None: dest_fn = self.blog_image_name()
|
||||||
|
img = cv2.imread(self.path)
|
||||||
|
h, w = img.shape[:2]
|
||||||
|
if w <= target_width:
|
||||||
|
cv2.imwrite(dest_fn, img)
|
||||||
|
return dest_fn
|
||||||
|
ratio = target_width / float(w)
|
||||||
|
new_h = int(h * ratio)
|
||||||
|
new_img = cv2.resize(img, (target_width, new_h), interpolation=cv2.INTER_AREA)
|
||||||
|
cv2.imwrite(dest_fn, new_img)
|
||||||
|
self.resized = True
|
||||||
|
|
||||||
|
def markdown_template(self, template):
|
||||||
|
img_replace = {
|
||||||
|
"{{IMG_FN}}": self.blog_image_name(),
|
||||||
|
"{{IMG_ORIG_FN}}": os.path.basename(self.path),
|
||||||
|
"{{IMG_FILE_DATE}}": markdown_date(self.file_date),
|
||||||
|
"{{IMG_META_DATE}}": self.metadata().get('DateTime', 'No Metadata')
|
||||||
|
}
|
||||||
|
for k in img_replace:
|
||||||
|
template = template.replace(k, img_replace[k])
|
||||||
|
return template
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
return {
|
||||||
|
"path": os.path.basename(self.path),
|
||||||
|
"metadata": self._metadata,
|
||||||
|
"resized": self.resized,
|
||||||
|
"resize_path": self.blog_image_name() if self.resized else '',
|
||||||
|
"date": self.file_date
|
||||||
|
}
|
||||||
|
|
||||||
|
def deserialize(self, data, directory):
|
||||||
|
self.path = os.path.join(directory, data['path'])
|
||||||
|
self._metadata = data['metadata']
|
||||||
|
self.resized = data['resized']
|
||||||
|
if data['date']: os.stat(self.path).st_ctime if self.path else None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<BlogImage path={os.path.basename(self.path)}, article_number={self.article_number}, image_number={self.image_number}>"
|
||||||
|
|
||||||
|
def best_date(self):
|
||||||
|
meta_date = self.metadata().get('DateTime')
|
||||||
|
if meta_date:
|
||||||
|
try:
|
||||||
|
return datetime.strptime(meta_date, '%Y:%m:%d %H:%M:%S').timestamp()
|
||||||
|
except:
|
||||||
|
print(f'DateTime exif failed parsing: {meta_date} ({self.path})')
|
||||||
|
if self.file_date:
|
||||||
|
return self.file_date
|
||||||
|
return -1
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.best_date() < other.best_date()
|
||||||
|
|
||||||
class BlogScaffold:
|
class BlogScaffold:
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
|
@ -32,6 +95,7 @@ class BlogScaffold:
|
||||||
self.data = { "images": [],
|
self.data = { "images": [],
|
||||||
"blogfile": None
|
"blogfile": None
|
||||||
}
|
}
|
||||||
|
self.blog_images = []
|
||||||
# Check the path for backblog metadata
|
# Check the path for backblog metadata
|
||||||
self.scanned = os.path.exists(os.path.join(self.path, "backblog.json"))
|
self.scanned = os.path.exists(os.path.join(self.path, "backblog.json"))
|
||||||
if not self.scanned:
|
if not self.scanned:
|
||||||
|
@ -42,12 +106,12 @@ class BlogScaffold:
|
||||||
|
|
||||||
def scan(self):
|
def scan(self):
|
||||||
_, _, files = next(os.walk(self.path))
|
_, _, files = next(os.walk(self.path))
|
||||||
|
self.blog_images = []
|
||||||
for f in files:
|
for f in files:
|
||||||
self.data['images'].append({
|
if f.split('.')[1].lower() not in ('jpg', 'png', 'bmp', 'jpeg'): continue
|
||||||
"path": f,
|
self.blog_images.append(BlogImage(os.path.join(self.path, f)))
|
||||||
"date": os.stat(os.path.join(self.path, f)).st_ctime
|
|
||||||
})
|
|
||||||
self.scanned = True
|
self.scanned = True
|
||||||
|
self.data['images'] = [bi.serialize() for bi in self.blog_images]
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
|
@ -59,23 +123,34 @@ class BlogScaffold:
|
||||||
yield datetime.fromtimestamp(i['date'])
|
yield datetime.fromtimestamp(i['date'])
|
||||||
|
|
||||||
def markdown_template(self, article_number):
|
def markdown_template(self, article_number):
|
||||||
|
if not self.scanned: self.scan()
|
||||||
replace = {
|
replace = {
|
||||||
"{{TITLE}}": "Backblog basic template about " + self.path,
|
"{{TITLE}}": "Backblog basic template about " + self.path,
|
||||||
"{{SLUG}}": os.path.basename(self.path),
|
"{{SLUG}}": os.path.basename(self.path),
|
||||||
"{{CATEGORY}}": "category",
|
"{{CATEGORY}}": "category",
|
||||||
"{{EARLIESTDATE}}": markdown_date(min([i['date'] for i in self.data['images']])),
|
"{{EARLIESTDATE}}": markdown_date(min([i['date'] for i in self.data['images']])),
|
||||||
"{{TODAY}}": str(date.today()),
|
"{{TODAY}}": str(date.today()),
|
||||||
"{{ARTICLENUM}}": article_number.zfill(2)
|
"{{ARTICLENUM}}": str(article_number).zfill(2)
|
||||||
}
|
}
|
||||||
txt = None
|
txt = None
|
||||||
with open("template.md", "r") as f:
|
with open("template.md", "r") as f:
|
||||||
txt = f.read()
|
txt = f.read()
|
||||||
img_template = txt.split("%%%")[1]
|
img_template = txt.split("%%%")[1]
|
||||||
img_txt = ""
|
img_txt = ""
|
||||||
for i, image in enumerate(self.data['images']):
|
for i, image in enumerate(sorted(self.blog_images)):
|
||||||
img_fn = resize_rename(image['path'], article_number, i)
|
image.article_number = article_number
|
||||||
this_txt = img_template
|
image.image_number = i
|
||||||
this_txt.replace("{{IMG_FN}}", img_fn)
|
image.resize(IMG_WIDTH)
|
||||||
|
img_txt += image.markdown_template(img_template)
|
||||||
|
txt = txt.split("%%%")[0] + img_txt + txt.split("%%%")[2]
|
||||||
|
for k in replace:
|
||||||
|
txt = txt.replace(k, replace[k])
|
||||||
|
template_fn = f"{str(article_number).zfill(2)}_{os.path.basename(self.path)}.md"
|
||||||
|
render_template_fn = os.path.join(self.path, template_fn)
|
||||||
|
with open(render_template_fn, "w") as f:
|
||||||
|
f.write(txt)
|
||||||
|
self.data['blogfile'] = template_fn
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if not self.scanned:
|
if not self.scanned:
|
||||||
|
@ -86,5 +161,9 @@ if __name__ == '__main__':
|
||||||
subdirs = os.listdir('..')
|
subdirs = os.listdir('..')
|
||||||
# don't scan program's directory
|
# don't scan program's directory
|
||||||
subdirs.remove(os.path.basename(os.path.abspath('.')))
|
subdirs.remove(os.path.basename(os.path.abspath('.')))
|
||||||
|
#os.chdir('..')
|
||||||
scaffolds = [BlogScaffold(os.path.join('..', sd)) for sd in subdirs]
|
scaffolds = [BlogScaffold(os.path.join('..', sd)) for sd in subdirs]
|
||||||
|
|
||||||
|
scaffolds.sort(key = lambda s: s.blog_images[-1], reverse=True)
|
||||||
|
for s in scaffolds:
|
||||||
|
print(f"{os.path.basename(s.path)} - earliest {markdown_date(s.blog_images[0].best_date())} - latest {markdown_date(s.blog_images[-1].best_date())}")
|
||||||
|
|
Loading…
Reference in New Issue