Skip to content

Commit e7ad723

Browse files
authored
Merge pull request #62 from ktt-ol/feature/better-albums
feat: previous/next on albums and reproducible results
2 parents 5a0b6f2 + a0f0552 commit e7ad723

File tree

2 files changed

+125
-34
lines changed

2 files changed

+125
-34
lines changed

albums.py

Lines changed: 120 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
import os
55
import shutil
66
import tempfile
7-
import time
8-
from datetime import datetime
7+
from datetime import datetime, timedelta
98
from os.path import join as join_path
109
from threading import Thread
1110

1211
import requests
1312
from slugify import slugify
1413

14+
albums_web_path = "images/albums"
1515
albums_path = "content/images/albums"
1616
cover_path = "album_covers"
1717
albums_url = "https://www.mainframe.io/media/album-images"
@@ -26,7 +26,14 @@ def join_folder_path(path_components: list[str]) -> str:
2626
return os.path.sep.join(slugged_path_components)
2727

2828

29-
def join_web_path(path_components: list[str]) -> str:
29+
def join_web_path(path_components: list[str], use_slug: bool = False) -> str:
30+
if use_slug:
31+
slugged = []
32+
for component in path_components:
33+
slugged.append(slugify(component))
34+
35+
path_components = slugged
36+
3037
return '/'.join(path_components)
3138

3239

@@ -70,7 +77,7 @@ def create_index_md(path_components: list[str], tmp_folder_albums: str, tmp_fold
7077
f.write('title = "' + title + '"\n')
7178
f.write('template = "album/album-list.html"\n')
7279
f.write('sort_by = "date"\n')
73-
f.write('weight = ' + str(int(time.mktime(created_at.timetuple()))) + '\n')
80+
f.write('weight = ' + str(int(created_at.timestamp() * 1000)) + '\n')
7481

7582
f.write('[extra]\n')
7683
f.write('display_name = "' + title + '"\n')
@@ -84,18 +91,22 @@ def create_index_md(path_components: list[str], tmp_folder_albums: str, tmp_fold
8491
f.close()
8592

8693

94+
def generate_image_filename(path_components: list[str], lang: str, index: int) -> str:
95+
filename = f"img{index:03d}" + lang + ".md"
96+
path = join_path(join_folder_path(path_components), filename)
97+
98+
return path
99+
100+
87101
def create_image_md(path_components: list[str], tmp_folder_albums: str, metadata: dict, created_at: datetime,
88-
lang: str) -> None:
102+
lang: str, index: int, image_count: int) -> None:
89103
if lang != "":
90104
lang = "." + lang
91105

92106
base_uri = join_web_path(path_components)
93107

94-
date_str = created_at.strftime("%Y-%m-%d")
95-
time_str = created_at.strftime("%H:%M:%S")
96-
97-
page_filename = date_str + "-" + slugify(metadata["filename"]) + lang + ".md"
98-
image_file_path = join_path(tmp_folder_albums, join_folder_path(path_components), page_filename)
108+
filename = generate_image_filename(path_components, lang, index)
109+
image_file_path = join_path(tmp_folder_albums, filename)
99110

100111
if os.path.exists(image_file_path):
101112
raise FileExistsError(image_file_path)
@@ -105,56 +116,129 @@ def create_image_md(path_components: list[str], tmp_folder_albums: str, metadata
105116
image_file.write("+++\n")
106117
image_file.write('title = "' + metadata["filename"] + '"\n')
107118
image_file.write('template = "album/album-single.html"\n')
108-
image_file.write('sort_by = "title"\n')
109-
image_file.write('date = ' + date_str + "T" + time_str + '\n')
119+
image_file.write('date = "' + created_at.strftime("%Y-%m-%dT%H:%M:%S") + '"\n')
120+
121+
# add old path as alias
122+
alias = "/".join([albums_web_path, join_web_path(path_components, True), slugify(metadata["filename"])])
123+
image_file.write('aliases = ["/' + alias + '"]\n')
110124

111125
image_file.write('[extra]\n')
112126
image_file.write('filename = "' + metadata["filename"] + '"\n')
113127
image_file.write('height = ' + str(metadata["height"]) + "\n")
114128
image_file.write('width = ' + str(metadata["width"]) + "\n")
115129
image_file.write('file_uri = "' + albums_url + "/" + base_uri + "/" + metadata["filename"] + '"\n')
116-
image_file.write('file_uri_300 = "' + albums_url + "/" + base_uri + "/.thumbs/300-" + metadata["filename"] + '"\n')
117130

118131
# Albums do not have 750 pixel thumbnail, use 1200 instead
119-
image_file.write('file_uri_750 = "' + albums_url + "/" + base_uri + "/.thumbs/1200-" + metadata["filename"] + '"\n')
120-
image_file.write(
121-
'file_uri_1200 = "' + albums_url + "/" + base_uri + "/.thumbs/1200-" + metadata["filename"] + '"\n')
132+
thumbs_base = albums_url + "/" + base_uri + "/.thumbs/"
133+
image_file.write('file_uri_300 = "' + thumbs_base + "300-" + metadata["filename"] + '"\n')
134+
image_file.write('file_uri_750 = "' + thumbs_base + "1200-" + metadata["filename"] + '"\n')
135+
image_file.write('file_uri_1200 = "' + thumbs_base + "1200-" + metadata["filename"] + '"\n')
136+
137+
if index > 0:
138+
prev_filename_web = generate_image_filename(path_components, lang, index - 1).replace(os.path.sep, "/")
139+
image_file.write(
140+
'previous = "/' + albums_web_path + "/" + prev_filename_web + '"\n')
141+
142+
if (index + 1) < image_count:
143+
next_filename_web = generate_image_filename(path_components, lang, index + 1).replace(os.path.sep, "/")
144+
image_file.write(
145+
'next = "/' + albums_web_path + "/" + next_filename_web + '"\n')
146+
122147
image_file.write("+++\n")
123148
image_file.close()
124149

125150

151+
def normalize_images(images_metadata: list[dict]) -> list[tuple[dict, datetime]]:
152+
images = []
153+
current_date = None
154+
155+
for image_metadata in images_metadata:
156+
if image_metadata["exif"]["time"] is not None:
157+
new_date = datetime.fromtimestamp(int(image_metadata["exif"]["time"]) / 1000)
158+
159+
if current_date is None or new_date > current_date:
160+
current_date = new_date
161+
162+
if current_date is None:
163+
image_created_at = datetime.fromtimestamp(0)
164+
else:
165+
image_created_at = current_date
166+
167+
images.append((image_metadata, image_created_at))
168+
169+
images.sort(key=lambda x: (x[1], x[0]["filename"]))
170+
171+
return images
172+
173+
174+
def normalize_folders(folder_metadata: list[dict]) -> list[tuple[dict, datetime]]:
175+
folders = []
176+
177+
for folder in folder_metadata:
178+
if folder["time"] is not None:
179+
new_date = datetime.fromtimestamp(folder["time"] / 1000)
180+
else:
181+
print("'" + folder["title"] + "' is missing a date")
182+
new_date = datetime.fromtimestamp(0)
183+
184+
folders.append((folder, new_date))
185+
186+
folders.sort(key=lambda x: (x[1], x[0]["foldername"]))
187+
188+
# Ensure that each album has its own unique timestamp associated with it to allow unique stubs and
189+
# deterministic list outputs in zola as it does not support sorting by more than one property
190+
unique_folders = []
191+
last_timestamp = None
192+
last_folder = None
193+
for folder, ts in folders:
194+
if last_folder is None:
195+
last_folder = folder
196+
if last_timestamp is not None and ts <= last_timestamp:
197+
new_ts = last_timestamp + timedelta(hours=1)
198+
print("Adjusting '" + folder["title"] + "' from '" + str(ts) + "' (cause: '"
199+
+ last_folder["title"] + "') to '" + str(new_ts) + "'")
200+
ts = new_ts
201+
else:
202+
last_folder = folder
203+
204+
unique_folders.append((folder, ts))
205+
last_timestamp = ts
206+
folder["time"] = ts
207+
208+
folders.sort(key=lambda x: (x[1], x[0]["foldername"]))
209+
210+
return unique_folders
211+
212+
126213
def create_album_folder(path_components: list[str], tmp_folder_albums: str, tmp_folder_covers: str,
127214
metadata: dict) -> None:
128215
path_components = path_components.copy()
129216
path_components.append(metadata["foldername"])
130217

131-
if metadata['time'] is not None:
132-
album_created_at = datetime.fromtimestamp(int(metadata["time"]) / 1000)
133-
else:
134-
print("Album has no creation date: ", path_components)
135-
album_created_at = datetime.fromtimestamp(0)
136-
137218
joined_path = join_folder_path(path_components)
138219

139220
album_folder = join_path(tmp_folder_albums, joined_path)
140221
os.makedirs(album_folder)
141222

142-
create_index_md(path_components, tmp_folder_albums, tmp_folder_covers, metadata, album_created_at, "")
223+
create_index_md(path_components, tmp_folder_albums, tmp_folder_covers, metadata, metadata["time"], "")
143224

144225
current_metadata = get_url_metadata(path_components)
145-
for image_metadata in current_metadata["images"]:
146-
if image_metadata['exif']['time'] is not None:
147-
image_created_at = datetime.fromtimestamp(int(image_metadata['exif']['time']) / 1000)
148-
else:
149-
image_created_at = datetime.fromtimestamp(0)
150226

151-
create_image_md(path_components, tmp_folder_albums, image_metadata, image_created_at, "")
227+
images = normalize_images(current_metadata["images"])
228+
229+
for i, (image_metadata, image_created_at) in enumerate(images):
230+
create_image_md(path_components, tmp_folder_albums, image_metadata, image_created_at, "", i, len(images))
152231

153232
sub_threads = []
154233

155-
for sub_directory in current_metadata["subDirs"]:
234+
sub_folders = normalize_folders(current_metadata["subDirs"])
235+
236+
for (sub_folder, timestamp) in sub_folders:
237+
# Use the normalized timestamp for the subfolder so that album_created_at
238+
# is based on the adjusted, uniqueness-enforced value in recursive calls.
239+
sub_folder["time"] = timestamp
156240
sub_thread = Thread(target=create_album_folder,
157-
args=(path_components, tmp_folder_albums, tmp_folder_covers, sub_directory))
241+
args=(path_components, tmp_folder_albums, tmp_folder_covers, sub_folder))
158242
sub_thread.start()
159243
sub_threads.append(sub_thread)
160244

@@ -168,9 +252,12 @@ def create_album_folder(path_components: list[str], tmp_folder_albums: str, tmp_
168252
tmp_ktt_ol_cover = tempfile.mkdtemp(prefix="ktt_ol_cover")
169253

170254
threads = []
171-
for directory in base_metadata["subDirs"]:
255+
256+
base_sub_folders = normalize_folders(base_metadata["subDirs"])
257+
258+
for (base_sub_folder, timestamp) in base_sub_folders:
172259
thread = Thread(target=create_album_folder,
173-
args=([], tmp_ktt_ol_albums, tmp_ktt_ol_cover, directory)
260+
args=([], tmp_ktt_ol_albums, tmp_ktt_ol_cover, base_sub_folder)
174261
)
175262
thread.start()
176263
threads.append(thread)

templates/album/album-list.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ <h3 class="d-flex justify-content-between">
5757

5858
{% set scaling = 200 / latest_page.extra.height %}
5959
{% set height = 200 %}
60-
{% set width = latest_page.extra.width * scaling %}
60+
{% set width = latest_page.extra.width * scaling %}
6161

6262
{% if width > 1150 %}
6363
{% set image_uri = latest_page.extra.file_uri %}
@@ -70,6 +70,8 @@ <h3 class="d-flex justify-content-between">
7070
{% endif %}
7171
{% endif %}
7272

73+
{% set width = width | round(method="ceil", precision=0) %}
74+
7375
<a href="{{ macros::rel_url(path=subsection_data.path, lang=lang) }}"
7476
class="text-decoration-none rounded-1 p-0 overflow-hidden border border-1"
7577
style="height: {{ height }}px; width: {{ width }}px;">
@@ -119,6 +121,8 @@ <h3 class="p-0 m-0 text-white border-0"><span
119121
{% set image_uri = page.extra.file_uri_300 %}
120122
{% endif %}
121123

124+
{% set width = width | round(method="ceil", precision=0) %}
125+
122126
<a class="text-decoration-none" href="{{ macros::rel_url(path=page.path, lang=lang) }}"
123127
style="height: {{ height }}px; width: {{ width }}px;">
124128
<img alt=""

0 commit comments

Comments
 (0)