瀏覽代碼

Automatic gallery and download generation script

Lars 1 年之前
父節點
當前提交
2a9458645b
共有 4 個文件被更改,包括 268 次插入10 次删除
  1. 2 0
      .gitignore
  2. 0 10
      content/nature/index.md
  3. 64 0
      galleries.yaml
  4. 202 0
      make_galleries.py

+ 2 - 0
.gitignore

@@ -1,3 +1,5 @@
 public/*
 resources/*
 *.lock
+content/*/
+downloads/*

+ 0 - 10
content/nature/index.md

@@ -1,10 +0,0 @@
----
-# description: 
-featured_image: Pretty sun on road_hq.jpg
-menus: "main"
-sort_by: Name # Exif.Date
-sort_order: desc
-title: Nature
-#type: gallery
-weight: 3
----

+ 64 - 0
galleries.yaml

@@ -0,0 +1,64 @@
+
+
+
+#freezing cold = ["DSC_2743", "DSC_2744", "DSC_2757"]
+#nature = ["DSC_2273", "DSC_2296", "DSC_2300", "DSC_2911"]
+#nice_view = ["DSC_2395", "DSC_2617", "DSC_2619", "DSC_2661", "DSC_2705"]
+#various = ["DSC_0263", "DSC_2341", "DSC_3079", "DSC_2323", "DSC_3073", "DSC_0897", "DSC_2769"]
+#textures = ["DSC_2932", "DSC_2948", "DSC_3124", "DSC_3161", "DSC_3212", "DSC_3485", "DSC_3487"]
+
+picture_root: "/home/lars/Nasbox/Media/BestBest"
+
+galleries:
+  - folder_name: cold-out-here
+    title: So cold out here
+    description: Snowy foggy landscapes have a special charm
+    cover_photo: "2024-Q1/DSC_2744"
+    photos:
+      - "2024-Q1/DSC_2743"
+      - "2024-Q1/DSC_2744"
+      - "2024-Q1/DSC_2757"
+
+  - folder_name: nature
+    title: Nature
+    cover_photo: "2024-Q1/DSC_2300"
+    photos:
+      - "2024-Q1/DSC_2273"
+      - "2024-Q1/DSC_2297"
+      - "2024-Q1/DSC_2300"
+      - "2024-Q1/DSC_2911"
+
+  - folder_name: nice-view
+    title: Nice view
+    photos:
+      - "2024-Q1/DSC_2395"
+      - "2024-Q1/DSC_2617"
+      - "2024-Q1/DSC_2619"
+      - "2024-Q1/DSC_2661"
+      - "2024-Q1/DSC_2705"
+  
+  - folder_name: various
+    title: "Various"
+    cover_photo: "2024-Q1/DSC_0263"
+    photos:
+      - "2024-Q1/DSC_0263"
+      - "2024-Q1/DSC_2341"
+      - "2024-Q1/DSC_3079"
+      - "2024-Q1/DSC_2323"
+      - "2024-Q1/DSC_3073"
+      - "2024-Q1/DSC_0897"
+      - "2024-Q1/DSC_2769"
+
+  - folder_name: texture-like
+    title: Texture Like
+    description: Images with a texture like quality
+    cover_photo: "2024-Q1/DSC_3124"
+    photos:
+      - "2024-Q1/DSC_2932"
+      - "2024-Q1/DSC_2948"
+      - "2024-Q1/DSC_3124"
+      - "2024-Q1/DSC_3161"
+      - "2024-Q1/DSC_3212"
+      - "2024-Q1/DSC_3485"
+      - "2024-Q1/DSC_3487"
+

+ 202 - 0
make_galleries.py

@@ -0,0 +1,202 @@
+#!/usr/bin/python3
+
+from typing import List, Optional
+from pydantic import BaseModel
+import yaml
+import shutil
+import zipfile
+import os
+import sys
+from datetime import datetime as dt
+
+# ----
+
+yaml_filename = 'galleries.yaml'
+content_dir = "./content"
+zip_dir = "./downloads"
+
+# ----
+
+def makeThankYou(gallery:str, date_generated:str):
+    return """Thank you for purchasing the "%s" gallery from larsee.com/photography!
+
+You have the rights to use the material in this download for any purpose.
+
+This archive was generated: %s. Note that the gallery contents might change in the future.
+
+Don't hesitate to contact me if you have questions or other inquiries. This is my private email: lars@f-w.dk
+
+Sincerely,
+Lars Ole Pontoppidan
+""" % (gallery, date_generated)
+
+# ----
+
+class Gallery(BaseModel):
+    folder_name: str
+    title: str
+    description: Optional[str] = None
+    cover_photo: Optional[str] = None
+    photos: List[str]
+
+class Galleries(BaseModel):
+    picture_root: str
+    galleries: List[Gallery]
+
+
+def loadGalleries(yaml_file_path: str) -> Galleries:
+    with open(yaml_file_path, 'r') as file:
+        data = yaml.safe_load(file)
+    return Galleries(**data)
+
+def fileTitle(s:str) -> str:
+    sp = s.split("/")
+    return sp[-1]
+
+class Photo():
+    def __init__(self, photo_root:str, source_file:str, prefix:str, index:int):
+        sp = source_file.split("/")
+        self.photoPath = sp[0]
+        self.photoName = sp[1]
+        self.destName = prefix + "_%02d.jpg" % index
+        self.photoRoot = photo_root
+        
+    def getDestJpgName(self):
+        return self.destName
+    
+    def getHqFileSource(self):
+        return os.path.join(self.photoRoot, self.photoPath, "hq", self.photoName + "_hq.jpg")
+
+    def getRawFilePath(self):
+        opt1 = os.path.join(self.photoRoot, self.photoPath, self.photoName + ".NEF")
+        opt2 = os.path.join(self.photoRoot, self.photoPath, self.photoName + ".JPG")
+        if os.path.isfile(opt1):
+            return opt1
+        elif os.path.isfile(opt2):
+            return opt2
+        else:
+            raise Exception("Can't find source file for: " + self.photoName)
+    
+    def getPp3FilePath(self):
+        return self.getRawFilePath() + ".pp3"
+   
+    def getTifFilePath(self):
+        return os.path.join(self.photoRoot, self.photoPath, "converted", self.photoName + ".tif")
+
+
+class PhotosInGallery():
+    def __init__(self, photo_root:str, gallery:Gallery):
+        self.photos:List[Photo] = []
+        self.cover_photo:Optional[Photo] = None
+
+        for idx, photo in enumerate(gallery.photos, start=1):
+            p = Photo(photo_root, photo, gallery.folder_name, idx)
+            self.photos.append(p)
+            if photo == gallery.cover_photo:
+                self.cover_photo = p
+    
+
+def makeIndexFile(weight:int, gallery:Gallery, photos:PhotosInGallery):
+    values = {
+        "title" : gallery.title,
+        "weight" : weight,
+        "menus" : {"main" : {"weight" : weight}},
+    }
+    if gallery.description is not None:
+        values["description"] = gallery.description
+
+    if photos.cover_photo is not None:
+        values["featured_image"] = photos.cover_photo.getDestJpgName()
+
+    return "---\n" + yaml.dump(values, default_flow_style=False) + "---\n"
+
+def makeGalleries(yaml_root:Galleries):
+    print("Purging old galleries")
+    for item in os.listdir(content_dir):
+        item_path = os.path.join(content_dir, item)
+        if os.path.isdir(item_path):
+            print(f"  deleting folder: {item_path}")
+            shutil.rmtree(item_path)
+
+    print("Setting up galleries")
+    for idx, gallery in enumerate(yaml_root.galleries):
+        path = os.path.join(content_dir, gallery.folder_name)
+        weight = len(yaml_root.galleries) - idx
+        print("  making dir: " + path)
+        os.makedirs(path, exist_ok=True)
+
+        photos = PhotosInGallery(yaml_root.picture_root, gallery)
+
+        index = makeIndexFile(weight, gallery, photos)
+        with open(os.path.join(path, "index.md"), "w") as f:
+            f.write(index)
+
+        # Link in the pictures
+        for photo in photos.photos:
+            #os.symlink(photo.getHqFileSource(), os.path.join(path, photo.getSimpleJpgName()))
+            shutil.copyfile(photo.getHqFileSource(), os.path.join(path, photo.getDestJpgName()))
+
+def makeZipDownloads(yaml_root:Galleries):
+    
+    if os.path.isdir(zip_dir):
+        print("Purging old downloads")
+        shutil.rmtree(zip_dir)
+    
+    os.makedirs(zip_dir, exist_ok=True)
+
+    # Get the current date and time
+    dt_now = dt.now()
+
+    print("Making download zips, archive date: " + dt_now.strftime("%B %d, %Y"))
+
+    for gallery in yaml_root.galleries:
+        photos = PhotosInGallery(yaml_root.picture_root, gallery)
+
+        # Create a ZIP file for the gallery
+        zip_path = os.path.join(zip_dir, "Gallery %s.zip" % gallery.folder_name)
+        print("  making zip: " + zip_path)
+
+        #gallery_folder = "%s_%s" % (gallery.folder_name, dt_now.strftime("%b_%d_%Y"))
+        gallery_folder = gallery.folder_name
+
+        with zipfile.ZipFile(zip_path, 'w') as myzip:
+            def addToZip(path:str, sub_folder:Optional[str] = None):
+                if sub_folder is None:
+                    myzip.write(path, os.path.join(gallery_folder, os.path.basename(path)))
+                else:
+                    myzip.write(path, os.path.join(gallery_folder, sub_folder, os.path.basename(path)))
+
+            for photo in photos.photos:
+                addToZip(photo.getRawFilePath())
+                addToZip(photo.getPp3FilePath())
+                addToZip(photo.getTifFilePath(), "converted")
+        
+            myzip.writestr('Thank you.txt', makeThankYou(gallery.title, dt_now.strftime("%B %d, %Y")))
+
+def main(galleries:bool, zips:bool):
+    print("Loading " + yaml_filename)
+    yaml_root = loadGalleries(yaml_filename)
+
+    if galleries:
+        makeGalleries(yaml_root)
+    if zips:
+        makeZipDownloads(yaml_root)
+
+def usage():
+    print("""
+Usage:
+    make_galleries OPTIONS
+          
+Options:
+    -g     Make galleries
+    -z     Make download zips
+""")
+    
+if __name__ == "__main__":
+    zips = "-z" in sys.argv
+    galleries = "-g" in sys.argv
+
+    if zips == False and galleries == False:
+        usage()
+    else:
+        main(galleries, zips)