blurry-cli 0.12.0__tar.gz → 0.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/PKG-INFO +1 -1
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/__init__.py +140 -72
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/images.py +29 -21
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/markdown/__init__.py +2 -2
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/jinja_plugins/blurry_image_extension.py +1 -1
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/sitemap.py +1 -3
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/pyproject.toml +1 -1
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/LICENSE +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/README.md +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/__main__.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/async_typer.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/cli.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/constants.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/markdown/front_matter.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/markdown/renderer_functions/__init__.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/markdown/renderer_functions/render_video.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/open_graph.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/__init__.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/html_plugins/__init__.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/html_plugins/minify_html_plugin.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/jinja_plugins/__init__.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/__init__.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/container_plugin.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/punctuation_plugin.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/python_code_plugin.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/py.typed +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/schema_validation.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/settings.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/templates.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/types.py +0 -0
- {blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/utils.py +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import concurrent.futures
|
|
2
3
|
import dataclasses
|
|
3
4
|
import importlib
|
|
4
5
|
import json
|
|
@@ -7,7 +8,6 @@ import os
|
|
|
7
8
|
import pkgutil
|
|
8
9
|
import shutil
|
|
9
10
|
import sys
|
|
10
|
-
from collections.abc import Coroutine
|
|
11
11
|
from copy import deepcopy
|
|
12
12
|
from datetime import datetime
|
|
13
13
|
from pathlib import Path
|
|
@@ -17,6 +17,10 @@ from jinja2 import Environment
|
|
|
17
17
|
from livereload import Server
|
|
18
18
|
from rich import print
|
|
19
19
|
from rich.console import Console
|
|
20
|
+
from rich.progress import BarColumn
|
|
21
|
+
from rich.progress import Progress
|
|
22
|
+
from rich.progress import TaskProgressColumn
|
|
23
|
+
from rich.progress import TextColumn
|
|
20
24
|
|
|
21
25
|
from blurry.async_typer import AsyncTyper
|
|
22
26
|
from blurry.cli import print_blurry_name
|
|
@@ -54,30 +58,31 @@ warning_console = Console(stderr=True, style="bold yellow")
|
|
|
54
58
|
app = AsyncTyper()
|
|
55
59
|
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
relative_filepath = filepath.relative_to(CONTENT_DIR)
|
|
61
|
-
build_filepath = get_build_directory() / relative_filepath
|
|
62
|
-
output_file = Path(build_filepath)
|
|
63
|
-
output_file.parent.mkdir(exist_ok=True, parents=True)
|
|
64
|
-
|
|
61
|
+
def process_non_markdown_file(
|
|
62
|
+
filepath: Path, file_data_by_directory, jinja_env: Environment
|
|
63
|
+
):
|
|
65
64
|
# Process Jinja files
|
|
66
65
|
if ".jinja" in filepath.suffixes:
|
|
67
66
|
# Process file
|
|
68
|
-
jinja_env = get_jinja_env()
|
|
69
67
|
process_jinja_file(filepath, jinja_env, file_data_by_directory)
|
|
70
68
|
return
|
|
71
69
|
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
CONTENT_DIR = get_content_directory()
|
|
71
|
+
mimetype, _ = mimetypes.guess_type(filepath, strict=False)
|
|
72
|
+
relative_filepath = filepath.relative_to(CONTENT_DIR)
|
|
73
|
+
output_file = get_build_directory() / relative_filepath
|
|
74
|
+
|
|
75
|
+
# Copy file to build directory if it is not already there
|
|
76
|
+
if not output_file.exists():
|
|
77
|
+
output_file.parent.mkdir(exist_ok=True, parents=True)
|
|
78
|
+
shutil.copyfile(filepath, output_file)
|
|
74
79
|
|
|
75
80
|
# Create srcset images
|
|
76
81
|
if mimetype in [
|
|
77
82
|
mimetypes.types_map[".jpg"],
|
|
78
83
|
mimetypes.types_map[".png"],
|
|
79
84
|
]:
|
|
80
|
-
|
|
85
|
+
asyncio.run(generate_images_for_srcset(filepath))
|
|
81
86
|
|
|
82
87
|
|
|
83
88
|
def process_jinja_file(filepath: Path, jinja_env: Environment, file_data_by_directory):
|
|
@@ -101,32 +106,35 @@ def process_jinja_file(filepath: Path, jinja_env: Environment, file_data_by_dire
|
|
|
101
106
|
filepath_in_build.write_text(html)
|
|
102
107
|
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
file_data_list: list[MarkdownFileData],
|
|
109
|
+
def write_html_file(
|
|
110
|
+
filepath: Path,
|
|
107
111
|
file_data_by_directory: dict[Path, list[MarkdownFileData]],
|
|
108
112
|
release: bool,
|
|
109
113
|
jinja_env: Environment,
|
|
110
114
|
):
|
|
111
115
|
extra_context: TemplateContext = {}
|
|
112
116
|
# Gather data from other files in this directory if this is an index file
|
|
113
|
-
|
|
117
|
+
file_data_list = file_data_by_directory[filepath.parent]
|
|
118
|
+
if filepath.name == "index.md":
|
|
114
119
|
sibling_pages = [
|
|
115
120
|
{
|
|
116
121
|
"url": content_path_to_url(f.path),
|
|
117
122
|
**f.front_matter,
|
|
118
123
|
}
|
|
119
124
|
for f in file_data_list
|
|
120
|
-
if f.path !=
|
|
125
|
+
if f.path != filepath
|
|
121
126
|
]
|
|
122
127
|
extra_context["sibling_pages"] = sibling_pages
|
|
123
|
-
folder_in_build = convert_content_path_to_directory_in_build(
|
|
128
|
+
folder_in_build = convert_content_path_to_directory_in_build(filepath)
|
|
124
129
|
|
|
130
|
+
file_data = [
|
|
131
|
+
f for f in file_data_by_directory[filepath.parent] if f.path == filepath
|
|
132
|
+
][0]
|
|
125
133
|
schema_type = file_data.front_matter.get("@type")
|
|
126
134
|
if not schema_type:
|
|
127
135
|
raise ValueError(
|
|
128
136
|
f"Required @type value missing in file or TOML front matter invalid: "
|
|
129
|
-
f"{
|
|
137
|
+
f"{filepath}"
|
|
130
138
|
)
|
|
131
139
|
template_extension = SETTINGS["MARKDOWN_FILE_JINJA_TEMPLATE_EXTENSION"]
|
|
132
140
|
template = jinja_env.get_template(f"{schema_type}{template_extension}")
|
|
@@ -152,12 +160,13 @@ async def write_html_file(
|
|
|
152
160
|
default=json_converter_with_dates,
|
|
153
161
|
)
|
|
154
162
|
|
|
155
|
-
validate_front_matter_as_schema(
|
|
163
|
+
validate_front_matter_as_schema(filepath, front_matter, warning_console)
|
|
156
164
|
|
|
157
165
|
schema_type_tag = f'<script type="application/ld+json">{schema_data}</script>'
|
|
158
166
|
|
|
159
167
|
template_context = {
|
|
160
168
|
"body": file_data.body,
|
|
169
|
+
"filepath": filepath,
|
|
161
170
|
"schema_data": schema_data,
|
|
162
171
|
"schema_type_tag": schema_type_tag,
|
|
163
172
|
"open_graph_tags": open_graph_meta_tags(file_data.front_matter),
|
|
@@ -182,34 +191,15 @@ async def write_html_file(
|
|
|
182
191
|
write_index_file_creating_path(folder_in_build, html)
|
|
183
192
|
|
|
184
193
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
"""Generates HTML content from Markdown files."""
|
|
188
|
-
|
|
189
|
-
print_blurry_name()
|
|
190
|
-
print_plugin_table()
|
|
191
|
-
|
|
192
|
-
update_settings()
|
|
193
|
-
jinja_env = get_jinja_env()
|
|
194
|
-
os.environ.setdefault(f"{ENV_VAR_PREFIX}BUILD_MODE", "prod" if release else "dev")
|
|
195
|
-
CONTENT_DIR = get_content_directory()
|
|
196
|
-
BUILD_DIR = get_build_directory()
|
|
197
|
-
start = datetime.now()
|
|
198
|
-
path = Path(CONTENT_DIR)
|
|
194
|
+
def gather_file_data_by_directory() -> DirectoryFileData:
|
|
195
|
+
# Sort file data by publishedDate/createdDate, descending, if present
|
|
199
196
|
file_data_by_directory: DirectoryFileData = {}
|
|
200
|
-
|
|
201
|
-
BUILD_DIR.mkdir(parents=True)
|
|
202
|
-
|
|
203
|
-
markdown_tasks: list[Coroutine] = []
|
|
204
|
-
non_markdown_tasks: list[Coroutine] = []
|
|
197
|
+
content_directory = get_content_directory()
|
|
205
198
|
|
|
206
|
-
|
|
207
|
-
for filepath in path.glob("**/*.md"):
|
|
199
|
+
for filepath in content_directory.glob("**/*.md"):
|
|
208
200
|
# Extract filepath for storing context data and writing out
|
|
209
|
-
relative_filepath = filepath.relative_to(
|
|
201
|
+
relative_filepath = filepath.relative_to(content_directory)
|
|
210
202
|
directory = relative_filepath.parent
|
|
211
|
-
if directory not in file_data_by_directory:
|
|
212
|
-
file_data_by_directory[directory] = []
|
|
213
203
|
|
|
214
204
|
# Convert Markdown file to HTML
|
|
215
205
|
body, front_matter = convert_markdown_file_to_html(filepath)
|
|
@@ -218,41 +208,79 @@ async def build(release=True):
|
|
|
218
208
|
front_matter=front_matter,
|
|
219
209
|
path=relative_filepath,
|
|
220
210
|
)
|
|
221
|
-
|
|
211
|
+
try:
|
|
212
|
+
file_data_by_directory[directory].append(file_data)
|
|
213
|
+
except KeyError:
|
|
214
|
+
file_data_by_directory[directory] = [file_data]
|
|
222
215
|
|
|
223
|
-
|
|
224
|
-
for filepath in path.glob("**/*"):
|
|
225
|
-
if filepath.suffix == ".md" or filepath.is_dir():
|
|
226
|
-
continue
|
|
227
|
-
non_markdown_tasks.append(
|
|
228
|
-
process_non_markdown_file(filepath, file_data_by_directory)
|
|
229
|
-
)
|
|
216
|
+
return sort_directory_file_data_by_date(file_data_by_directory)
|
|
230
217
|
|
|
231
|
-
# Sort file data by publishedDate/createdDate, descending, if present
|
|
232
|
-
file_data_by_directory = sort_directory_file_data_by_date(file_data_by_directory)
|
|
233
218
|
|
|
234
|
-
|
|
219
|
+
@app.async_command()
|
|
220
|
+
async def build(release=True):
|
|
221
|
+
"""Generates HTML content from Markdown files."""
|
|
222
|
+
start = datetime.now()
|
|
223
|
+
|
|
224
|
+
if release:
|
|
225
|
+
print_blurry_name()
|
|
226
|
+
print_plugin_table()
|
|
227
|
+
|
|
228
|
+
update_settings()
|
|
229
|
+
content_directory = get_content_directory()
|
|
230
|
+
build_directory = get_build_directory()
|
|
231
|
+
build_directory.mkdir(parents=True, exist_ok=True)
|
|
232
|
+
jinja_env = get_jinja_env()
|
|
235
233
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
234
|
+
os.environ.setdefault(f"{ENV_VAR_PREFIX}BUILD_MODE", "prod" if release else "dev")
|
|
235
|
+
|
|
236
|
+
file_data_by_directory = gather_file_data_by_directory()
|
|
237
|
+
|
|
238
|
+
markdown_task_count = 0
|
|
239
|
+
non_markdown_task_count = 1 # sitemap
|
|
240
|
+
|
|
241
|
+
with Progress(
|
|
242
|
+
TextColumn("[progress.description]{task.description}"),
|
|
243
|
+
BarColumn(),
|
|
244
|
+
TaskProgressColumn(),
|
|
245
|
+
transient=True,
|
|
246
|
+
) as progress:
|
|
247
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
248
|
+
markdown_task = progress.add_task("[blue]Markdown")
|
|
249
|
+
other_task = progress.add_task("[blue]Other")
|
|
250
|
+
|
|
251
|
+
sitemap_future = executor.submit(write_sitemap_file, file_data_by_directory)
|
|
252
|
+
sitemap_future.add_done_callback(lambda _: progress.advance(other_task))
|
|
253
|
+
|
|
254
|
+
for filepath in content_directory.rglob("*"):
|
|
255
|
+
if filepath.is_dir():
|
|
256
|
+
continue
|
|
257
|
+
if filepath.suffix == ".md":
|
|
258
|
+
markdown_future = executor.submit(
|
|
259
|
+
write_html_file,
|
|
260
|
+
filepath.relative_to(content_directory),
|
|
261
|
+
file_data_by_directory,
|
|
262
|
+
release,
|
|
263
|
+
jinja_env,
|
|
264
|
+
)
|
|
265
|
+
markdown_task_count += 1
|
|
266
|
+
progress.update(markdown_task, total=markdown_task_count)
|
|
267
|
+
markdown_future.add_done_callback(
|
|
268
|
+
lambda _: progress.advance(markdown_task)
|
|
269
|
+
)
|
|
270
|
+
continue
|
|
271
|
+
other_future = executor.submit(
|
|
272
|
+
process_non_markdown_file,
|
|
273
|
+
filepath,
|
|
242
274
|
file_data_by_directory,
|
|
243
|
-
release,
|
|
244
275
|
jinja_env,
|
|
245
276
|
)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
print(f"Blurring {len(markdown_tasks)} Markdown files from: {content_dir_relative}")
|
|
277
|
+
non_markdown_task_count += 1
|
|
278
|
+
progress.update(other_task, total=non_markdown_task_count)
|
|
279
|
+
other_future.add_done_callback(lambda _: progress.advance(other_task))
|
|
250
280
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
for non_markdown_task in non_markdown_tasks:
|
|
255
|
-
await asyncio.to_thread(lambda: loop.run_until_complete(non_markdown_task))
|
|
281
|
+
print(
|
|
282
|
+
f"Blurring {markdown_task_count} Markdown files and {non_markdown_task_count} other files"
|
|
283
|
+
)
|
|
256
284
|
|
|
257
285
|
end = datetime.now()
|
|
258
286
|
|
|
@@ -281,6 +309,9 @@ async def reload_local_plugins_and_build():
|
|
|
281
309
|
@app.command()
|
|
282
310
|
def runserver():
|
|
283
311
|
"""Starts HTTP server with live reloading."""
|
|
312
|
+
print_blurry_name()
|
|
313
|
+
print_plugin_table()
|
|
314
|
+
|
|
284
315
|
update_settings()
|
|
285
316
|
os.environ.setdefault(f"{ENV_VAR_PREFIX}BUILD_MODE", "dev")
|
|
286
317
|
|
|
@@ -289,14 +320,51 @@ def runserver():
|
|
|
289
320
|
event_loop = asyncio.get_event_loop()
|
|
290
321
|
event_loop.create_task(build_development())
|
|
291
322
|
|
|
323
|
+
jinja_env = get_jinja_env()
|
|
324
|
+
|
|
325
|
+
def handle_changed_jinja_files(filepaths: list[str]):
|
|
326
|
+
file_data_by_directory = gather_file_data_by_directory()
|
|
327
|
+
for filepath in filepaths:
|
|
328
|
+
process_jinja_file(
|
|
329
|
+
Path.cwd() / filepath,
|
|
330
|
+
jinja_env,
|
|
331
|
+
file_data_by_directory,
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
def handle_changed_markdown_files(filepaths: list[str]):
|
|
335
|
+
file_data_by_directory = gather_file_data_by_directory()
|
|
336
|
+
content_directory = get_content_directory()
|
|
337
|
+
for filepath in filepaths:
|
|
338
|
+
write_html_file(
|
|
339
|
+
filepath=(Path.cwd() / filepath).relative_to(content_directory),
|
|
340
|
+
file_data_by_directory=file_data_by_directory,
|
|
341
|
+
release=False,
|
|
342
|
+
jinja_env=jinja_env,
|
|
343
|
+
)
|
|
344
|
+
|
|
292
345
|
livereload_server = Server()
|
|
346
|
+
livereload_server.watch(
|
|
347
|
+
f"{SETTINGS['CONTENT_DIRECTORY_NAME']}/**/*.jinja", handle_changed_jinja_files
|
|
348
|
+
)
|
|
349
|
+
livereload_server.watch(
|
|
350
|
+
f"{SETTINGS['CONTENT_DIRECTORY_NAME']}/**/*.md",
|
|
351
|
+
handle_changed_markdown_files,
|
|
352
|
+
)
|
|
293
353
|
livereload_server.watch(
|
|
294
354
|
f"{SETTINGS['CONTENT_DIRECTORY_NAME']}/**/*",
|
|
295
355
|
lambda: event_loop.create_task(build_development()),
|
|
356
|
+
ignore=lambda filepath: any(
|
|
357
|
+
[
|
|
358
|
+
filepath.endswith(".md"),
|
|
359
|
+
filepath.endswith(".jinja"),
|
|
360
|
+
Path(filepath).is_dir(),
|
|
361
|
+
]
|
|
362
|
+
),
|
|
296
363
|
)
|
|
297
364
|
livereload_server.watch(
|
|
298
365
|
f"{SETTINGS['TEMPLATES_DIRECTORY_NAME']}/**/*",
|
|
299
366
|
lambda: event_loop.create_task(build_development()),
|
|
367
|
+
ignore=lambda filepath: Path(filepath).is_dir(),
|
|
300
368
|
)
|
|
301
369
|
livereload_server.watch(
|
|
302
370
|
"./**/*.py",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import concurrent.futures
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
4
|
from wand.image import Image
|
|
@@ -30,7 +30,7 @@ def add_image_width_to_path(image_path: Path, width: int) -> Path:
|
|
|
30
30
|
return Path(new_filename)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
def convert_image_to_avif(image_path: Path, target_path: Path | None = None):
|
|
34
34
|
AVIF_COMPRESSION_QUALITY = SETTINGS["AVIF_COMPRESSION_QUALITY"]
|
|
35
35
|
image_suffix = image_path.suffix
|
|
36
36
|
avif_filepath = str(target_path or image_path).replace(image_suffix, ".avif")
|
|
@@ -42,31 +42,39 @@ async def convert_image_to_avif(image_path: Path, target_path: Path | None = Non
|
|
|
42
42
|
image.save(filename=avif_filepath)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
def clone_and_resize_image(
|
|
46
|
+
image: Image, target_width: int, resized_image_destination: Path
|
|
47
|
+
):
|
|
48
|
+
if resized_image_destination.exists():
|
|
49
|
+
return
|
|
50
|
+
image.transform(resize=str(target_width))
|
|
51
|
+
image.save(filename=resized_image_destination)
|
|
52
|
+
|
|
53
|
+
|
|
45
54
|
async def generate_images_for_srcset(image_path: Path):
|
|
46
55
|
BUILD_DIR = get_build_directory()
|
|
47
56
|
CONTENT_DIR = get_content_directory()
|
|
57
|
+
filepaths_to_convert_to_avif = []
|
|
58
|
+
|
|
59
|
+
build_path = BUILD_DIR / image_path.resolve().relative_to(CONTENT_DIR)
|
|
60
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
61
|
+
with Image(filename=str(image_path)) as img:
|
|
62
|
+
width = img.width
|
|
63
|
+
|
|
64
|
+
for target_width in get_widths_for_image_width(width):
|
|
65
|
+
new_filepath = add_image_width_to_path(image_path, target_width)
|
|
66
|
+
relative_filepath = new_filepath.resolve().relative_to(CONTENT_DIR)
|
|
67
|
+
build_filepath = BUILD_DIR / relative_filepath
|
|
68
|
+
# We convert the resized images to AVIF, so do this synchronously
|
|
69
|
+
clone_and_resize_image(img, target_width, build_filepath)
|
|
70
|
+
filepaths_to_convert_to_avif.append(build_filepath)
|
|
48
71
|
|
|
49
|
-
with Image(filename=str(image_path)) as img:
|
|
50
|
-
width = img.width
|
|
51
72
|
# Convert original image
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
avif_files_to_create: list[Path] = []
|
|
56
|
-
for target_width in get_widths_for_image_width(width):
|
|
57
|
-
new_filepath = add_image_width_to_path(image_path, target_width)
|
|
58
|
-
relative_filepath = new_filepath.resolve().relative_to(CONTENT_DIR)
|
|
59
|
-
build_filepath = BUILD_DIR / relative_filepath
|
|
60
|
-
with img.clone() as resized:
|
|
61
|
-
avif_files_to_create.append(build_filepath)
|
|
62
|
-
if build_filepath.exists():
|
|
63
|
-
continue
|
|
64
|
-
resized.transform(resize=str(target_width))
|
|
65
|
-
resized.save(filename=build_filepath)
|
|
66
|
-
|
|
67
|
-
await asyncio.gather(
|
|
68
|
-
*[convert_image_to_avif(avif_file) for avif_file in avif_files_to_create]
|
|
73
|
+
executor.submit(
|
|
74
|
+
convert_image_to_avif, image_path=image_path, target_path=build_path
|
|
69
75
|
)
|
|
76
|
+
# Generate AVIF files for resized images
|
|
77
|
+
executor.map(convert_image_to_avif, filepaths_to_convert_to_avif)
|
|
70
78
|
|
|
71
79
|
|
|
72
80
|
def get_widths_for_image_width(image_width: int) -> list[int]:
|
|
@@ -84,7 +84,7 @@ class BlurryRenderer(mistune.HTMLRenderer):
|
|
|
84
84
|
attributes_str = " ".join(
|
|
85
85
|
f'{name}="{value}"' for name, value in attributes.items()
|
|
86
86
|
)
|
|
87
|
-
return f"<img {attributes_str}
|
|
87
|
+
return f"<img {attributes_str}>"
|
|
88
88
|
|
|
89
89
|
image_widths = get_widths_for_image_width(image_width)
|
|
90
90
|
|
|
@@ -103,7 +103,7 @@ class BlurryRenderer(mistune.HTMLRenderer):
|
|
|
103
103
|
|
|
104
104
|
return (
|
|
105
105
|
f"<figure>"
|
|
106
|
-
f"<picture>{source_tag}<img {attributes_str}
|
|
106
|
+
f"<picture>{source_tag}<img {attributes_str}></picture>"
|
|
107
107
|
f'<figcaption>{attributes["alt"]}</figcaption>'
|
|
108
108
|
f"</figure>"
|
|
109
109
|
)
|
|
@@ -38,9 +38,7 @@ def generate_sitemap_for_file_data_list(file_data_list: list[MarkdownFileData])
|
|
|
38
38
|
return SITEMAP_TEMPLATE.format(urls=sitemap_url_content)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
file_data_by_directory: dict[Path, list[MarkdownFileData]]
|
|
43
|
-
):
|
|
41
|
+
def write_sitemap_file(file_data_by_directory: dict[Path, list[MarkdownFileData]]):
|
|
44
42
|
BUILD_DIR = get_build_directory()
|
|
45
43
|
file_data = []
|
|
46
44
|
for file_data_list in file_data_by_directory.values():
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/punctuation_plugin.py
RENAMED
|
File without changes
|
{blurry_cli-0.12.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/python_code_plugin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|