blurry-cli 0.11.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.11.0 → blurry_cli-0.13.0}/PKG-INFO +1 -1
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/__init__.py +165 -67
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/images.py +29 -21
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/markdown/__init__.py +2 -2
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/jinja_plugins/blurry_image_extension.py +1 -1
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/sitemap.py +1 -3
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/templates.py +6 -5
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/pyproject.toml +1 -1
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/LICENSE +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/README.md +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/__main__.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/async_typer.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/cli.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/constants.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/markdown/front_matter.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/markdown/renderer_functions/__init__.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/markdown/renderer_functions/render_video.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/open_graph.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/__init__.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/html_plugins/__init__.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/html_plugins/minify_html_plugin.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/jinja_plugins/__init__.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/__init__.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/container_plugin.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/punctuation_plugin.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/python_code_plugin.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/py.typed +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/schema_validation.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/settings.py +0 -0
- {blurry_cli-0.11.0 → blurry_cli-0.13.0}/blurry/types.py +0 -0
- {blurry_cli-0.11.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,51 +58,83 @@ warning_console = Console(stderr=True, style="bold yellow")
|
|
|
54
58
|
app = AsyncTyper()
|
|
55
59
|
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
def process_non_markdown_file(
|
|
62
|
+
filepath: Path, file_data_by_directory, jinja_env: Environment
|
|
63
|
+
):
|
|
64
|
+
# Process Jinja files
|
|
65
|
+
if ".jinja" in filepath.suffixes:
|
|
66
|
+
# Process file
|
|
67
|
+
process_jinja_file(filepath, jinja_env, file_data_by_directory)
|
|
68
|
+
return
|
|
69
|
+
|
|
58
70
|
CONTENT_DIR = get_content_directory()
|
|
59
71
|
mimetype, _ = mimetypes.guess_type(filepath, strict=False)
|
|
60
72
|
relative_filepath = filepath.relative_to(CONTENT_DIR)
|
|
61
|
-
|
|
62
|
-
output_file = Path(build_filepath)
|
|
63
|
-
output_file.parent.mkdir(exist_ok=True, parents=True)
|
|
73
|
+
output_file = get_build_directory() / relative_filepath
|
|
64
74
|
|
|
65
|
-
# Copy file to build directory
|
|
66
|
-
|
|
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)
|
|
67
79
|
|
|
68
80
|
# Create srcset images
|
|
69
81
|
if mimetype in [
|
|
70
82
|
mimetypes.types_map[".jpg"],
|
|
71
83
|
mimetypes.types_map[".png"],
|
|
72
84
|
]:
|
|
73
|
-
|
|
85
|
+
asyncio.run(generate_images_for_srcset(filepath))
|
|
74
86
|
|
|
75
87
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
def process_jinja_file(filepath: Path, jinja_env: Environment, file_data_by_directory):
|
|
89
|
+
build_directory = get_build_directory()
|
|
90
|
+
content_directory = get_content_directory()
|
|
91
|
+
template = jinja_env.get_template(str(filepath.relative_to(content_directory)))
|
|
92
|
+
context = {
|
|
93
|
+
"file_data_by_directory": {
|
|
94
|
+
str(path): data for path, data in deepcopy(file_data_by_directory).items()
|
|
95
|
+
},
|
|
96
|
+
"settings": deepcopy(SETTINGS),
|
|
97
|
+
"datetime": datetime,
|
|
98
|
+
}
|
|
99
|
+
filepath_with_new_extension = filepath.with_suffix(
|
|
100
|
+
filepath.suffix.replace(".jinja", "")
|
|
101
|
+
)
|
|
102
|
+
filepath_in_build = build_directory / filepath_with_new_extension.relative_to(
|
|
103
|
+
content_directory
|
|
104
|
+
)
|
|
105
|
+
html = template.render(dataclasses=dataclasses, **context)
|
|
106
|
+
filepath_in_build.write_text(html)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def write_html_file(
|
|
110
|
+
filepath: Path,
|
|
79
111
|
file_data_by_directory: dict[Path, list[MarkdownFileData]],
|
|
80
112
|
release: bool,
|
|
81
113
|
jinja_env: Environment,
|
|
82
114
|
):
|
|
83
115
|
extra_context: TemplateContext = {}
|
|
84
116
|
# Gather data from other files in this directory if this is an index file
|
|
85
|
-
|
|
117
|
+
file_data_list = file_data_by_directory[filepath.parent]
|
|
118
|
+
if filepath.name == "index.md":
|
|
86
119
|
sibling_pages = [
|
|
87
120
|
{
|
|
88
121
|
"url": content_path_to_url(f.path),
|
|
89
122
|
**f.front_matter,
|
|
90
123
|
}
|
|
91
124
|
for f in file_data_list
|
|
92
|
-
if f.path !=
|
|
125
|
+
if f.path != filepath
|
|
93
126
|
]
|
|
94
127
|
extra_context["sibling_pages"] = sibling_pages
|
|
95
|
-
folder_in_build = convert_content_path_to_directory_in_build(
|
|
128
|
+
folder_in_build = convert_content_path_to_directory_in_build(filepath)
|
|
96
129
|
|
|
130
|
+
file_data = [
|
|
131
|
+
f for f in file_data_by_directory[filepath.parent] if f.path == filepath
|
|
132
|
+
][0]
|
|
97
133
|
schema_type = file_data.front_matter.get("@type")
|
|
98
134
|
if not schema_type:
|
|
99
135
|
raise ValueError(
|
|
100
136
|
f"Required @type value missing in file or TOML front matter invalid: "
|
|
101
|
-
f"{
|
|
137
|
+
f"{filepath}"
|
|
102
138
|
)
|
|
103
139
|
template_extension = SETTINGS["MARKDOWN_FILE_JINJA_TEMPLATE_EXTENSION"]
|
|
104
140
|
template = jinja_env.get_template(f"{schema_type}{template_extension}")
|
|
@@ -124,12 +160,13 @@ async def write_html_file(
|
|
|
124
160
|
default=json_converter_with_dates,
|
|
125
161
|
)
|
|
126
162
|
|
|
127
|
-
validate_front_matter_as_schema(
|
|
163
|
+
validate_front_matter_as_schema(filepath, front_matter, warning_console)
|
|
128
164
|
|
|
129
165
|
schema_type_tag = f'<script type="application/ld+json">{schema_data}</script>'
|
|
130
166
|
|
|
131
167
|
template_context = {
|
|
132
168
|
"body": file_data.body,
|
|
169
|
+
"filepath": filepath,
|
|
133
170
|
"schema_data": schema_data,
|
|
134
171
|
"schema_type_tag": schema_type_tag,
|
|
135
172
|
"open_graph_tags": open_graph_meta_tags(file_data.front_matter),
|
|
@@ -154,40 +191,15 @@ async def write_html_file(
|
|
|
154
191
|
write_index_file_creating_path(folder_in_build, html)
|
|
155
192
|
|
|
156
193
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"""Generates HTML content from Markdown files."""
|
|
160
|
-
|
|
161
|
-
print_blurry_name()
|
|
162
|
-
print_plugin_table()
|
|
163
|
-
|
|
164
|
-
update_settings()
|
|
165
|
-
jinja_env = get_jinja_env()
|
|
166
|
-
os.environ.setdefault(f"{ENV_VAR_PREFIX}BUILD_MODE", "prod" if release else "dev")
|
|
167
|
-
CONTENT_DIR = get_content_directory()
|
|
168
|
-
BUILD_DIR = get_build_directory()
|
|
169
|
-
start = datetime.now()
|
|
170
|
-
path = Path(CONTENT_DIR)
|
|
194
|
+
def gather_file_data_by_directory() -> DirectoryFileData:
|
|
195
|
+
# Sort file data by publishedDate/createdDate, descending, if present
|
|
171
196
|
file_data_by_directory: DirectoryFileData = {}
|
|
172
|
-
|
|
173
|
-
BUILD_DIR.mkdir(parents=True)
|
|
174
|
-
|
|
175
|
-
markdown_tasks: list[Coroutine] = []
|
|
176
|
-
non_markdown_tasks: list[Coroutine] = []
|
|
177
|
-
|
|
178
|
-
for filepath in path.glob("**/*.*"):
|
|
179
|
-
# Handle images and other files
|
|
180
|
-
if filepath.suffix != ".md":
|
|
181
|
-
non_markdown_tasks.append(process_non_markdown_file(filepath))
|
|
182
|
-
continue
|
|
183
|
-
|
|
184
|
-
# Handle Markdown files
|
|
197
|
+
content_directory = get_content_directory()
|
|
185
198
|
|
|
199
|
+
for filepath in content_directory.glob("**/*.md"):
|
|
186
200
|
# Extract filepath for storing context data and writing out
|
|
187
|
-
relative_filepath = filepath.relative_to(
|
|
201
|
+
relative_filepath = filepath.relative_to(content_directory)
|
|
188
202
|
directory = relative_filepath.parent
|
|
189
|
-
if directory not in file_data_by_directory:
|
|
190
|
-
file_data_by_directory[directory] = []
|
|
191
203
|
|
|
192
204
|
# Convert Markdown file to HTML
|
|
193
205
|
body, front_matter = convert_markdown_file_to_html(filepath)
|
|
@@ -196,33 +208,79 @@ async def build(release=True):
|
|
|
196
208
|
front_matter=front_matter,
|
|
197
209
|
path=relative_filepath,
|
|
198
210
|
)
|
|
199
|
-
|
|
211
|
+
try:
|
|
212
|
+
file_data_by_directory[directory].append(file_data)
|
|
213
|
+
except KeyError:
|
|
214
|
+
file_data_by_directory[directory] = [file_data]
|
|
200
215
|
|
|
201
|
-
|
|
216
|
+
return sort_directory_file_data_by_date(file_data_by_directory)
|
|
202
217
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
218
|
+
|
|
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()
|
|
233
|
+
|
|
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,
|
|
212
274
|
file_data_by_directory,
|
|
213
|
-
release,
|
|
214
275
|
jinja_env,
|
|
215
276
|
)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
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))
|
|
220
280
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
for non_markdown_task in non_markdown_tasks:
|
|
225
|
-
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
|
+
)
|
|
226
284
|
|
|
227
285
|
end = datetime.now()
|
|
228
286
|
|
|
@@ -251,6 +309,9 @@ async def reload_local_plugins_and_build():
|
|
|
251
309
|
@app.command()
|
|
252
310
|
def runserver():
|
|
253
311
|
"""Starts HTTP server with live reloading."""
|
|
312
|
+
print_blurry_name()
|
|
313
|
+
print_plugin_table()
|
|
314
|
+
|
|
254
315
|
update_settings()
|
|
255
316
|
os.environ.setdefault(f"{ENV_VAR_PREFIX}BUILD_MODE", "dev")
|
|
256
317
|
|
|
@@ -259,14 +320,51 @@ def runserver():
|
|
|
259
320
|
event_loop = asyncio.get_event_loop()
|
|
260
321
|
event_loop.create_task(build_development())
|
|
261
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
|
+
|
|
262
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
|
+
)
|
|
263
353
|
livereload_server.watch(
|
|
264
354
|
f"{SETTINGS['CONTENT_DIRECTORY_NAME']}/**/*",
|
|
265
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
|
+
),
|
|
266
363
|
)
|
|
267
364
|
livereload_server.watch(
|
|
268
365
|
f"{SETTINGS['TEMPLATES_DIRECTORY_NAME']}/**/*",
|
|
269
366
|
lambda: event_loop.create_task(build_development()),
|
|
367
|
+
ignore=lambda filepath: Path(filepath).is_dir(),
|
|
270
368
|
)
|
|
271
369
|
livereload_server.watch(
|
|
272
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():
|
|
@@ -5,6 +5,7 @@ from jinja2 import select_autoescape
|
|
|
5
5
|
|
|
6
6
|
from blurry.plugins import discovered_jinja_extensions
|
|
7
7
|
from blurry.plugins import discovered_jinja_filter_plugins
|
|
8
|
+
from blurry.settings import get_content_directory
|
|
8
9
|
from blurry.settings import get_templates_directory
|
|
9
10
|
from blurry.settings import SETTINGS
|
|
10
11
|
|
|
@@ -14,16 +15,16 @@ def get_jinja_env() -> Environment:
|
|
|
14
15
|
Returns a Jinja environment complete with JinjaX and any installed Jinja extensions and filters.
|
|
15
16
|
"""
|
|
16
17
|
templates_directory = get_templates_directory()
|
|
18
|
+
content_directory = get_content_directory()
|
|
17
19
|
jinja_env = Environment(
|
|
18
|
-
loader=FileSystemLoader(templates_directory),
|
|
20
|
+
loader=FileSystemLoader([content_directory, templates_directory]),
|
|
19
21
|
autoescape=select_autoescape(
|
|
20
|
-
list(
|
|
22
|
+
enabled_extensions=list(
|
|
21
23
|
{
|
|
22
24
|
SETTINGS["MARKDOWN_FILE_JINJA_TEMPLATE_EXTENSION"].lstrip("."),
|
|
23
|
-
"
|
|
24
|
-
"xml",
|
|
25
|
+
"jinja",
|
|
25
26
|
}
|
|
26
|
-
)
|
|
27
|
+
),
|
|
27
28
|
),
|
|
28
29
|
extensions=[
|
|
29
30
|
jinja_extension.load() for jinja_extension in discovered_jinja_extensions
|
|
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.11.0 → blurry_cli-0.13.0}/blurry/plugins/markdown_plugins/punctuation_plugin.py
RENAMED
|
File without changes
|
{blurry_cli-0.11.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
|