djhx-blogger 0.1.0__py3-none-any.whl → 0.1.9__py3-none-any.whl

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.

Potentially problematic release.


This version of djhx-blogger might be problematic. Click here for more details.

djhx_blogger/cli.py CHANGED
@@ -3,16 +3,24 @@ from pathlib import Path
3
3
  import typer
4
4
 
5
5
  from .deploy import compress_dir, deploy_blog
6
- from .gen import generate_blog
7
- from .log_config import log_init
6
+ from .gen import generate_blog, init_new_blog
7
+ from .log_config import log_init, app_logger
8
+
8
9
  log_init()
9
10
 
11
+ logger = app_logger
12
+
10
13
  app = typer.Typer()
11
14
 
12
15
 
13
16
  @app.command()
14
17
  def run(
15
- origin: Path = typer.Argument(..., exists=True, readable=True, help="原始博客内容目录(必填)"),
18
+ # origin: Path = typer.Argument(..., exists=True, readable=True, help="原始博客内容目录(必填)"),
19
+ origin: Path = typer.Option(
20
+ None,
21
+ "--origin", '-o',
22
+ help='静态模块目录源地址',
23
+ ),
16
24
  target: Path = typer.Option(
17
25
  Path.cwd(),
18
26
  "--target", "-t",
@@ -33,6 +41,11 @@ def run(
33
41
  "--deploy/--no-deploy",
34
42
  help="是否将静态博客部署到远程服务器。",
35
43
  ),
44
+ new_blog: Path = typer.Option(
45
+ None,
46
+ "--new-blog", "-n",
47
+ help="生成一个简单的示例博客",
48
+ )
36
49
  ):
37
50
  typer.echo(f"原始目录: {origin}")
38
51
  typer.echo(f"输出目录: {target}")
@@ -40,7 +53,16 @@ def run(
40
53
  typer.echo(f"目标服务器部署地址: {server_target}")
41
54
  typer.echo(f"是否部署: {'是' if deploy else '否'}")
42
55
 
43
- root_node = generate_blog(str(origin))
56
+ if new_blog:
57
+ logger.info(f'在 {new_blog} 下生成一个新的博客目录')
58
+ init_new_blog(str(new_blog))
59
+ return
60
+
61
+ if not origin:
62
+ logger.warning(f'需要指定 origin')
63
+ return
64
+
65
+ root_node = generate_blog(str(origin), str(target))
44
66
 
45
67
  if deploy and server and server_target:
46
68
  tar_path = compress_dir(root_node.destination_path)
djhx_blogger/gen.py CHANGED
@@ -1,10 +1,12 @@
1
+ import os
1
2
  import shutil
2
3
  import time
3
- from collections import deque, OrderedDict
4
+ from collections import deque, OrderedDict, defaultdict
4
5
  from importlib import resources
5
6
  from pathlib import Path
6
7
 
7
8
  import markdown
9
+ from PIL import Image
8
10
  from bs4 import BeautifulSoup
9
11
  from jinja2 import Template
10
12
 
@@ -19,6 +21,10 @@ def load_template(name: str) -> str:
19
21
  file_path = resources.files("djhx_blogger.static.template").joinpath(name)
20
22
  return file_path.read_text(encoding="utf-8")
21
23
 
24
+ def load_image(img_name: str) -> Path:
25
+ file_path = resources.files("djhx_blogger.static.images").joinpath(img_name)
26
+ return Path(str(file_path))
27
+
22
28
 
23
29
  class Node:
24
30
  cache_map = {}
@@ -44,11 +50,12 @@ class Node:
44
50
  return f'path={self.source_path}'
45
51
 
46
52
 
47
- def walk_dir(dir_path_str: str, destination_blog_dir_name: str) -> Node:
53
+ def walk_dir(dir_path_str: str, destination_blog_dir_path_str: str, target_name: str='public') -> Node:
48
54
  """
49
55
  遍历目录,构造树结构
50
56
  :param dir_path_str: 存放博客 md 文件的目录的字符串
51
- :param destination_blog_dir_name: 生成博客目录的名称
57
+ :param destination_blog_dir_path_str: 生成博客目录的地址
58
+ :param target_name: 生成博客的目录名称
52
59
  :return: 树结构的根节点
53
60
  """
54
61
 
@@ -58,7 +65,7 @@ def walk_dir(dir_path_str: str, destination_blog_dir_name: str) -> Node:
58
65
  q.append(dir_path)
59
66
 
60
67
  # 生成目录的根路径
61
- destination_root_dir = dir_path.parent.joinpath(destination_blog_dir_name)
68
+ destination_root_dir = Path(destination_blog_dir_path_str).joinpath(target_name)
62
69
  logger.info(f'源路经: {dir_path}, 目标路径: {destination_root_dir}')
63
70
 
64
71
  root = None
@@ -253,13 +260,18 @@ def gen_blog_dir(root: Node):
253
260
  with open(node.destination_path, mode='w', encoding='utf-8') as f:
254
261
  f.write(gen_article_index(node.source_path, node.source_path.parent.name))
255
262
  else:
256
- shutil.copy(node.source_path, node.destination_path)
263
+ # shutil.copy(node.source_path, node.destination_path)
264
+ # 图片压缩
265
+ start_time = int(time.time() * 1000)
266
+ compress_image(node.source_path, node.destination_path)
267
+ end_time = int(time.time() * 1000)
268
+ logger.info(f'压缩图片耗时: {(end_time-start_time)} ms | {node.source_path} -> {node.destination_path}')
257
269
 
258
270
  end = int(time.time() * 1000)
259
271
  logger.info(f'生成目标目录耗时: {end - start} ms')
260
272
 
261
273
 
262
- def gen_blog_archive(blog_dir_str, public_name, root: Node):
274
+ def gen_blog_archive(blog_dir_str: str, blog_target_dir_str: str, root: Node, target_name: str='public'):
263
275
  """
264
276
  生成博客 archive 页面
265
277
  按照年份分栏,日期排序,展示所有的博客文章
@@ -284,7 +296,7 @@ def gen_blog_archive(blog_dir_str, public_name, root: Node):
284
296
  for article in articles_sorted:
285
297
  article_name = article.source_path.name
286
298
  full_path = article.destination_path / Path('index.html')
287
- base_path = blog_dir.with_name(public_name)
299
+ base_path = Path(blog_target_dir_str) / Path(target_name)
288
300
  url = full_path.relative_to(base_path)
289
301
 
290
302
  article_datetime = article.metadata.get('date')
@@ -310,10 +322,9 @@ def gen_blog_archive(blog_dir_str, public_name, root: Node):
310
322
  root_node_path.joinpath('archive.html').write_text(data=html, encoding='utf-8')
311
323
 
312
324
 
313
- def cp_resource(dir_path_str: str):
325
+ def cp_resource(blog_target_path_str: str):
314
326
  """将包内 static 资源复制到目标目录下的 public/"""
315
- dir_path = Path(dir_path_str)
316
- public_dir = dir_path.parent / "public"
327
+ public_dir = Path(blog_target_path_str) / "public"
317
328
 
318
329
  # 1. 复制 css/
319
330
  css_src = str(resources.files("djhx_blogger.static").joinpath("css"))
@@ -352,16 +363,161 @@ def parse_metadata(metadata):
352
363
  return meta_dict
353
364
 
354
365
 
355
- def generate_blog(blog_dir: str):
366
+
367
+ def compress_image(input_path, output_path, quality=70, max_size=(960, 540)):
368
+ """
369
+ 压缩图片到指定质量和最大尺寸。
370
+ - input_path: 源图片路径
371
+ - output_path: 输出路径,默认覆盖源文件
372
+ - quality: 压缩质量(0~100)
373
+ - max_size: 限制最大宽高(超过则等比缩小)
374
+ """
375
+ if not input_path or not output_path:
376
+ logger.warning(f'图片压缩 input/output path 不能为空')
377
+ return
378
+
379
+ with Image.open(input_path).convert("RGB") as img:
380
+ img.thumbnail(max_size)
381
+ img.save(output_path, optimize=True, quality=quality)
382
+
383
+
384
+ def analyze_directory_size(directory_path):
385
+ """
386
+ 分析目录下不同类型文件的数量和占用空间
387
+
388
+ 参数:
389
+ directory_path: 要分析的目录路径
390
+
391
+ 返回:
392
+ dict: 包含文件类型统计信息的字典
393
+ """
394
+ # 存储统计结果的字典
395
+ file_stats = defaultdict(lambda: {'count': 0, 'size_bytes': 0})
396
+
397
+ # 遍历目录及其所有子目录
398
+ for root, dirs, files in os.walk(directory_path):
399
+ for file in files:
400
+ file_path = os.path.join(root, file)
401
+
402
+ try:
403
+ # 获取文件大小
404
+ file_size = os.path.getsize(file_path)
405
+
406
+ # 获取文件扩展名(转换为小写,去掉点)
407
+ file_ext = Path(file).suffix.lower()
408
+ if not file_ext:
409
+ file_ext = '无扩展名'
410
+ else:
411
+ file_ext = file_ext[1:] # 去掉前面的点
412
+
413
+ # 更新统计信息
414
+ file_stats[file_ext]['count'] += 1
415
+ file_stats[file_ext]['size_bytes'] += file_size
416
+
417
+ except (OSError, IOError):
418
+ # 跳过无法访问的文件
419
+ continue
420
+
421
+ return file_stats
422
+
423
+
424
+ def format_size(size_bytes):
425
+ """
426
+ 将字节数转换为易读的格式
427
+
428
+ 参数:
429
+ size_bytes: 字节数
430
+
431
+ 返回:
432
+ str: 格式化后的大小字符串
433
+ """
434
+ if size_bytes == 0:
435
+ return "0B"
436
+
437
+ size_names = ["B", "KB", "MB", "GB", "TB"]
438
+ i = 0
439
+ while size_bytes >= 1024 and i < len(size_names) - 1:
440
+ size_bytes /= 1024.0
441
+ i += 1
442
+
443
+ # 根据大小选择合适的精度
444
+ if i == 0: # B
445
+ return f"{int(size_bytes)}{size_names[i]}"
446
+ elif i <= 2: # KB, MB
447
+ return f"{size_bytes:.1f}{size_names[i]}"
448
+ else: # GB, TB
449
+ return f"{size_bytes:.2f}{size_names[i]}"
450
+
451
+
452
+ def print_directory_stats(directory_path):
453
+ """
454
+ 打印目录统计信息
455
+
456
+ 参数:
457
+ directory_path: 要分析的目录路径
458
+ """
459
+ stats = analyze_directory_size(directory_path)
460
+
461
+ if not stats:
462
+ print("目录为空或无法访问")
463
+ return
464
+
465
+ # 按文件大小排序
466
+ sorted_stats = sorted(stats.items(), key=lambda x: x[1]['size_bytes'], reverse=True)
467
+
468
+ # 打印表头
469
+ print(f"{'类型':<10} | {'数量':<6} | {'大小':<10}")
470
+ print("-" * 30)
471
+
472
+ # 打印每种文件类型的统计信息
473
+ for file_type, data in sorted_stats:
474
+ count = data['count']
475
+ size_str = format_size(data['size_bytes'])
476
+ print(f"{file_type:<10} | {count:<6} | {size_str:<10}")
477
+
478
+ def generate_blog(blog_dir: str, blog_target: str):
356
479
  start = time.time()
357
- public_name = 'public'
358
480
 
359
481
  logger.info("开始生成博客文件结构...")
360
- root_node = walk_dir(blog_dir, public_name)
482
+ root_node = walk_dir(blog_dir, blog_target)
361
483
  gen_blog_dir(root_node)
362
- gen_blog_archive(blog_dir, public_name, root_node)
363
- cp_resource(str(blog_dir))
484
+ gen_blog_archive(blog_dir, blog_target, root_node)
485
+ cp_resource(blog_target)
364
486
 
365
487
  end = time.time()
366
- logger.info(f'生成静态博客 {blog_dir}, 任务完成, 总耗时: {int((end-start)*1000)} ms')
488
+ logger.info(f'生成静态博客 {blog_dir} -> {root_node.destination_path}, 任务完成, 总耗时: {int((end-start)*1000)} ms')
489
+ print_directory_stats(root_node.destination_path)
490
+
367
491
  return root_node
492
+
493
+
494
+ def init_new_blog(blog_dir: str):
495
+ blog_dir_path = Path(blog_dir) / "simple-blog" / "demo-article"
496
+ blog_images_dir_path = blog_dir_path / "images"
497
+ blog_dir_path.mkdir(parents=True, exist_ok=True)
498
+ blog_images_dir_path.mkdir(parents=True, exist_ok=True)
499
+ with open(blog_dir_path / 'index.md', 'w', encoding='utf-8') as file:
500
+ file.write(f"""---
501
+ title: "Demo Post"
502
+ date: 1970-01-01T08:00:00+08:00
503
+ summary: "simple demo article"
504
+ ---\n
505
+
506
+ # Hello world!\n
507
+
508
+ ## title 1
509
+
510
+ mountain images:
511
+
512
+ ![mountain](./images/mountain.jpg)
513
+
514
+ ### title 2
515
+
516
+ This is a simple demo...
517
+ """
518
+ )
519
+ file.write('')
520
+
521
+ mountain_img = load_image('mountain.jpg')
522
+ logger.info(mountain_img)
523
+ shutil.copy2(mountain_img, blog_images_dir_path / 'mountain.jpg')
@@ -0,0 +1,49 @@
1
+ body {
2
+ background-color: #121212;
3
+ color: #e0e0e0;
4
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+
9
+ .archive-container {
10
+ max-width: 800px;
11
+ margin: 2rem auto;
12
+ padding: 0 1rem;
13
+ }
14
+
15
+ .archive-title {
16
+ font-size: 2rem;
17
+ margin-bottom: 2rem;
18
+ text-align: center;
19
+ border-bottom: 1px solid #333;
20
+ padding-bottom: 0.5rem;
21
+ color: #ffffff;
22
+ }
23
+
24
+ .archive-year {
25
+ margin-bottom: 2rem;
26
+ }
27
+
28
+ .archive-year h2 {
29
+ font-size: 1.5rem;
30
+ color: #bb86fc;
31
+ border-left: 4px solid #bb86fc;
32
+ padding-left: 0.5rem;
33
+ margin-bottom: 0.5rem;
34
+ }
35
+
36
+ .archive-year ul {
37
+ list-style: none;
38
+ padding-left: 1rem;
39
+ }
40
+
41
+ .archive-item {
42
+ margin: 0.3rem 0;
43
+ font-size: 1rem;
44
+ color: #cccccc;
45
+ }
46
+
47
+ .archive-item a {
48
+ color: wheat;
49
+ }
@@ -0,0 +1,147 @@
1
+ body {
2
+ background-color: #121212;
3
+ }
4
+
5
+ article {
6
+ margin: 50px auto;
7
+ width: 50%;
8
+ background-color: #303030;
9
+ padding-left: 40px;
10
+ padding-right: 40px;
11
+ padding-bottom: 80px;
12
+ }
13
+
14
+ article h1 {
15
+ font-size: 3em;
16
+ text-align: center;
17
+ margin-top: 30px;
18
+ margin-bottom: 30px;
19
+ color: aliceblue;
20
+ }
21
+
22
+ article h2 {
23
+ font-size: 2.2em;
24
+ margin-top: 30px;
25
+ margin-bottom: 20px;
26
+ color: cadetblue;
27
+ }
28
+
29
+ article h3, article h4, article h5, article h6 {
30
+ font-size: 1.8em;
31
+ margin-top: 20px;
32
+ margin-bottom: 20px;
33
+ color: darkcyan;
34
+ }
35
+
36
+ article p {
37
+ font-size: 1.2em;
38
+ line-height: 2.2em;
39
+ margin-bottom: 20px;
40
+ color: wheat;
41
+ }
42
+
43
+ article img {
44
+ max-width: 100%; /* 限制图片宽度为容器宽度 */
45
+ height: auto; /* 保持图片比例 */
46
+ }
47
+
48
+ code {
49
+ border: 1px solid #ddd; /* 添加边框 */
50
+ border-radius: 4px; /* 圆角边框 */
51
+ font-family: monospace; /* 使用等宽字体 */
52
+ font-size: 1.2em; /* 稍微缩小字体 */
53
+ margin-top: 20px;
54
+ margin-bottom: 20px;
55
+ color: white;
56
+ padding: 3px;
57
+ }
58
+
59
+ blockquote {
60
+ background-color: dimgrey;
61
+ margin: 1em 0; /* 设置上下间距 */
62
+ padding: 0.5em 1em; /* 内边距 */
63
+ border-left: 4px solid #0074d9; /* 左侧蓝色边框 */
64
+ color: #555; /* 设置文本颜色 */
65
+ font-style: italic; /* 倾斜字体 */
66
+ }
67
+
68
+ table {
69
+ width: 100%;
70
+ border-collapse: collapse;
71
+ margin: 1em 0;
72
+ background-color: #1e1e1e; /* 表格整体背景 */
73
+ color: #ccc; /* 默认文字颜色 */
74
+ }
75
+
76
+ table th, table td {
77
+ border: 1px solid #444; /* 深色边框 */
78
+ padding: 8px;
79
+ text-align: left;
80
+ }
81
+
82
+ table th {
83
+ background-color: #2e2e2e; /* 表头背景色 */
84
+ color: #fff; /* 表头文字颜色 */
85
+ font-weight: bold;
86
+ }
87
+
88
+ table tr:nth-child(even) {
89
+ background-color: #2a2a2a; /* 偶数行背景色 */
90
+ }
91
+
92
+ table tr:nth-child(odd) {
93
+ background-color: #242424; /* 奇数行背景色 */
94
+ }
95
+
96
+ table tr:hover {
97
+ background-color: #555522; /* 鼠标悬停高亮色 */
98
+ }
99
+
100
+ ul, ol {
101
+ margin: 1em 0; /* 上下外边距 */
102
+ padding-left: 2em; /* 左内边距(缩进) */
103
+ line-height: 1.6; /* 设置行高 */
104
+ }
105
+
106
+ ul {
107
+ list-style-type: disc; /* 使用圆点作为项目符号 */
108
+ }
109
+
110
+ ol {
111
+ list-style-type: decimal; /* 使用数字作为编号 */
112
+ }
113
+
114
+ ul li, ol li {
115
+ margin-bottom: 0.5em; /* 列表项的下间距 */
116
+ }
117
+
118
+ ul li::marker {
119
+ color: #0074d9; /* 修改圆点颜色 */
120
+ }
121
+
122
+ ol li::marker {
123
+ color: #e74c3c; /* 修改数字颜色 */
124
+ }
125
+
126
+ li {
127
+ font-size: 1rem; /* 设置字体大小 */
128
+ color: aquamarine; /* 设置文字颜色 */
129
+ }
130
+
131
+ li:hover {
132
+ color: antiquewhite; /* 鼠标悬停时改变文字颜色 */
133
+ }
134
+
135
+ hr {
136
+ margin-top: 20px;
137
+ margin-bottom: 20px;
138
+ }
139
+
140
+ .article-meta time, .article-meta p {
141
+ font-size: 0.9em;
142
+ color: aquamarine;
143
+ }
144
+
145
+ a {
146
+ color: deeppink;
147
+ }
@@ -0,0 +1,53 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ nav {
8
+ display: flex;
9
+ justify-content: space-between;
10
+ align-items: center;
11
+ padding: 10px 20px;
12
+ background-color: #333;
13
+ color: #fff;
14
+ }
15
+
16
+ nav .brand {
17
+ font-size: 1.6em;
18
+ font-weight: bold;
19
+ text-decoration: none;
20
+ color: #4caf50;
21
+ }
22
+
23
+ nav .nav-links a {
24
+ margin-left: 15px;
25
+ text-decoration: none;
26
+ color: #fff;
27
+ transition: color 0.3s;
28
+ }
29
+
30
+ nav .nav-links a:hover {
31
+ color: #4caf50;
32
+ }
33
+
34
+ .site-footer {
35
+ background: #141414; /* 深色背景 */
36
+ padding: 0.8rem;
37
+ margin-top: 4rem; /* 可以移除或调整,因为使用了 fixed 定位 */
38
+ border-top: 2px solid #444;
39
+ text-align: center;
40
+ position: fixed; /* 将 footer 固定定位 */
41
+ bottom: 0; /* 固定在底部 */
42
+ left: 0; /* 从左侧开始 */
43
+ width: 100%; /* 宽度撑满整个视口 */
44
+ z-index: 10; /* 确保不被其他元素遮挡,可以根据实际情况调整 */
45
+ color: wheat;
46
+ font-size: 0.7rem;
47
+ }
48
+
49
+ .site-footer a {
50
+ color: #ffd700; /* 高亮备案链接 */
51
+ text-decoration: none;
52
+ font-weight: 500;
53
+ }
@@ -0,0 +1,112 @@
1
+ body {
2
+ background-color: #202020;
3
+
4
+ min-height: 100vh;
5
+ display: flex;
6
+ flex-direction: column;
7
+
8
+ }
9
+
10
+ h1 {
11
+ text-align: center;
12
+ color: moccasin;
13
+ margin-top: 3%;
14
+ margin-bottom: 3%;
15
+ }
16
+
17
+ h2 {
18
+ text-align: center;
19
+ color: rgb(121, 149, 146);
20
+ margin-top: 3%;
21
+ margin-bottom: 3%;
22
+ }
23
+
24
+ hr {
25
+ border: 0;
26
+ border-top: 1px dashed #a2a9b6;
27
+ margin-top: 3%;
28
+ margin-bottom: 3%;
29
+ }
30
+
31
+ /* 分类容器优化 */
32
+ .category-container {
33
+ display: grid;
34
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
35
+ gap: 2rem;
36
+ padding: 2rem 1rem;
37
+ max-width: 1200px;
38
+ margin: 2rem auto;
39
+ }
40
+
41
+ .category {
42
+ display: flex;
43
+ flex-direction: column;
44
+ align-items: center;
45
+ background: linear-gradient(145deg, #2d2d2d, #383838);
46
+ border-radius: 16px;
47
+ padding: 2rem;
48
+ transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
49
+ box-shadow: 0 8px 20px rgba(0,0,0,0.2);
50
+ backdrop-filter: blur(8px);
51
+ border: 1px solid rgba(255,255,255,0.05);
52
+ }
53
+
54
+ .category:hover {
55
+ transform: translateY(-8px);
56
+ box-shadow: 0 12px 30px rgba(76,175,80,0.15);
57
+ background: linear-gradient(145deg, #383838, #4d4d4d);
58
+ }
59
+
60
+ /* 文章卡片优化 */
61
+ .article {
62
+ display: flex;
63
+ flex-direction: column;
64
+ align-items: center;
65
+ background: linear-gradient(145deg, #2d2d2d, #383838);
66
+ border-radius: 16px;
67
+ padding: 2rem;
68
+ margin: 1.5rem 0;
69
+ transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
70
+ box-shadow: 0 8px 20px rgba(0,0,0,0.2);
71
+ backdrop-filter: blur(8px);
72
+ border: 1px solid rgba(255,255,255,0.05);
73
+ }
74
+
75
+ .article:hover {
76
+ transform: translateY(-5px);
77
+ box-shadow: 0 10px 25px rgba(76,175,80,0.1);
78
+ }
79
+
80
+ .article h2 {
81
+ color: rgb(255, 149, 146);
82
+ }
83
+
84
+ /* 链接样式优化 */
85
+ .item-link {
86
+ margin-top: 1.5rem;
87
+ display: flex;
88
+ gap: 1rem;
89
+ justify-content: center;
90
+ }
91
+
92
+ a {
93
+ font-size: 1.1em;
94
+ text-decoration: none;
95
+ color: #8b8ea3;
96
+ transition: all 0.3s ease;
97
+ padding: 0.5rem 1rem;
98
+ border-radius: 8px;
99
+ }
100
+
101
+ a:hover {
102
+ color: #4caf50;
103
+ background: rgba(76,175,80,0.1);
104
+ }
105
+
106
+ /* 元数据样式 */
107
+ .article-metadata {
108
+ color: #b0b0b0;
109
+ font-size: 0.9rem;
110
+ margin-top: 1rem;
111
+ text-align: center;
112
+ }
Binary file
Binary file
@@ -0,0 +1,40 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>归档</title>
6
+ <link rel="icon" type="image/x-icon" href="/blog/images/favicon.ico">
7
+ <link href="/blog/css/basic.css" rel="stylesheet">
8
+ <link href="/blog/css/archive.css" rel="stylesheet">
9
+ </head>
10
+ <body>
11
+ <nav>
12
+ <a href="/" class="brand">阿辉的小站</a>
13
+ <div class="nav-links">
14
+ <a href="/blog/index.html">blog</a>
15
+ <a href="/blog/archive.html">archive</a>
16
+ <a href="/blog/about/index.html">about</a>
17
+ <a href="/blog/hardware/index.html">hardware</a>
18
+ <a href="/blog/software/index.html">software</a>
19
+ <a href="/blog/cook/index.html">cook</a>
20
+ </div>
21
+ </nav>
22
+
23
+ <main class="archive-container">
24
+ <h1 class="archive-title">归档</h1>
25
+ {% for year, info in archives.items() %}
26
+ <section class="archive-year">
27
+ <h2>{{ year }} - 共计 {{ info.get('total') }} 篇</h2>
28
+ <ul>
29
+ {% for entry in info.get('articles') %}
30
+ <li class="archive-item">
31
+ <span class="date">{{ entry.date }}</span>:<a href="{{ entry.url }}">{{ entry.title }}</a>
32
+ </li>
33
+ {% endfor %}
34
+ </ul>
35
+ </section>
36
+ {% endfor %}
37
+ </main>
38
+
39
+ </body>
40
+ </html>
@@ -0,0 +1,39 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title></title>
6
+ <link rel="icon" type="image/x-icon" href="/blog/images/favicon.ico">
7
+ <link href="/blog/css/basic.css" rel="stylesheet">
8
+ <link href="/blog/css/article.css" rel="stylesheet">
9
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css"
10
+ rel="stylesheet"/>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
12
+ <script>
13
+ hljs.highlightAll();
14
+ </script>
15
+ </head>
16
+ <body>
17
+ <nav>
18
+ <a href="/" class="brand">阿辉的小站</a>
19
+ <div class="nav-links">
20
+ <a href="/blog/index.html">blog</a>
21
+ <a href="/blog/archive.html">archive</a>
22
+ <a href="/blog/about/index.html">about</a>
23
+ <a href="/blog/hardware/index.html">hardware</a>
24
+ <a href="/blog/software/index.html">software</a>
25
+ <a href="/blog/cook/index.html">cook</a>
26
+ </div>
27
+ </nav>
28
+
29
+ <article></article>
30
+ <footer>
31
+ <p class="site-footer">
32
+ 备案号:
33
+ <a href="https://beian.miit.gov.cn/" target="_blank">
34
+ 浙ICP备19051268号
35
+ </a>
36
+ </p>
37
+ </footer>
38
+ </body>
39
+ </html>
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>分类 | {{ category_name }}</title>
6
+ <link rel="icon" type="image/x-icon" href="/blog/images/favicon.ico">
7
+ <link href="/blog/css/basic.css" rel="stylesheet">
8
+ <link href="/blog/css/category.css" rel="stylesheet">
9
+ </head>
10
+ <body>
11
+ <nav>
12
+ <a href="/" class="brand">阿辉的小站</a>
13
+ <div class="nav-links">
14
+ <a href="/blog/index.html">blog</a>
15
+ <a href="/blog/archive.html">archive</a>
16
+ <a href="/blog/about/index.html">about</a>
17
+ <a href="/blog/hardware/index.html">hardware</a>
18
+ <a href="/blog/software/index.html">software</a>
19
+ <a href="/blog/cook/index.html">cook</a>
20
+ </div>
21
+ </nav>
22
+
23
+ <div class="category-container">
24
+ {% if not categories %}
25
+ <h1>暂无内容</h1>
26
+ {% endif %}
27
+
28
+ {% for item in categories %}
29
+
30
+ <div class="{{ 'article' if item['type'] == 'article' else 'category' }}">
31
+ <a href="{{ item['href'] }}">
32
+ <h2>
33
+ {{ item['name'] }}
34
+ </h2>
35
+ </a>
36
+ {% if item['metadata'] is not none %}
37
+ <div class="article-metadata">
38
+ <strong>Date:</strong> {{ item.metadata.date }}<br>
39
+ <strong>Summary:</strong> {{ item.metadata.summary }}
40
+ </div>
41
+ {% endif %}
42
+ </div>
43
+
44
+ {% endfor %}
45
+ </div>
46
+
47
+ <footer>
48
+ <p class="site-footer">
49
+ 备案号:
50
+ <a href="https://beian.miit.gov.cn/" target="_blank">
51
+ 浙ICP备19051268号
52
+ </a>
53
+ </p>
54
+ </footer>
55
+
56
+ </body>
57
+ </html>
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: djhx-blogger
3
+ Version: 0.1.9
4
+ Summary: A simple static site generator, support only markdown file.
5
+ Requires-Python: >=3.9
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE
8
+ Requires-Dist: beautifulsoup4>=4.14.2
9
+ Requires-Dist: fabric>=3.2.2
10
+ Requires-Dist: jinja2>=3.1.6
11
+ Requires-Dist: markdown>=3.9
12
+ Requires-Dist: pillow>=11.3.0
13
+ Requires-Dist: typer>=0.20.0
14
+ Dynamic: license-file
15
+
16
+ # djhx-blogger
17
+
18
+ 一个个人使用的博客生成器。
19
+
20
+ ## 命令
21
+
22
+ 查看帮助
23
+ ```shell
24
+ blg --help
25
+ ```
26
+
27
+ 生成一个示例博客目录
28
+
29
+ ```shell
30
+ blg -n "C:\Users\ABC\Desktop\"
31
+ ```
32
+
33
+ 生成静态页面
34
+
35
+ ```shell
36
+ blg -o "C:\Users\ABC\Desktop\simple-blog\"
37
+ ```
38
+
39
+ ## TODO
40
+
41
+ 1. 图片压缩
42
+ 2. 博客网站 title 和 nav-bar 的模板化
@@ -0,0 +1,21 @@
1
+ djhx_blogger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ djhx_blogger/__main__.py,sha256=MwMXMO3kuvG-luTG4lnLwjEnqnm20lI-amV9sETColo,61
3
+ djhx_blogger/cli.py,sha256=IT_hPolwqGsPbOLWwbQymfsHZtUyatgskXYue70UoFE,2013
4
+ djhx_blogger/deploy.py,sha256=tHJrOHaW8vQqN-YfpRLpjZOaUDXrWk_NVXnvPM014Hc,2056
5
+ djhx_blogger/gen.py,sha256=nfDws6xSHAnMP2SBGq6VkT-hWkD3wIclINJ-mx8FcJw,17291
6
+ djhx_blogger/log_config.py,sha256=aEvShHNahBO52w5I6WU4U2yTMAO7qIezcH2K1vSnbVo,871
7
+ djhx_blogger/static/css/archive.css,sha256=Jkfl7HlQ-zsRDT4nFaR0WR7m3-AwCXsWVlLWBLdWVNM,855
8
+ djhx_blogger/static/css/article.css,sha256=lCuMUOia2E7_E_OG7c2Gc7JMr-RdoyASQQ2mB6J2qKo,3239
9
+ djhx_blogger/static/css/basic.css,sha256=gSKoyM_3GwoNGg3wScrVNl9ccnZwkw_ffnxODu_wbko,1152
10
+ djhx_blogger/static/css/category.css,sha256=DBjUWZiaZO8uzpl2iY3acEVYzigW2HM3dHRfh2zNjok,2351
11
+ djhx_blogger/static/images/favicon.ico,sha256=N0NH9yMTUzEZhGomQKeYohKP1MmL5CfmtsZi3jdlbvU,45497
12
+ djhx_blogger/static/images/mountain.jpg,sha256=lY64nDTxXm86MEKMhSZ0FaZSmKQ72tlIb-8cRl6AeC8,766801
13
+ djhx_blogger/static/template/archive.html,sha256=aTeyirij9reMl1WmDTmZVHF0yip3c8RoAam4tj6RIo8,1340
14
+ djhx_blogger/static/template/article.html,sha256=GgU1mZrfAd-T3Y4Z0bqOljdkGI89p-OsuCAU4IHrTwE,1246
15
+ djhx_blogger/static/template/category.html,sha256=KRjTJw-8G3bs_LrhfeEm7xYtBON5v0pLKTPwf_PD_Uo,1607
16
+ djhx_blogger-0.1.9.dist-info/licenses/LICENSE,sha256=Whbb1w0-YAwWeAth-B6_jXSPWx9Fum73B0R-Z_lzUjA,1085
17
+ djhx_blogger-0.1.9.dist-info/METADATA,sha256=Jufi_kHTmoD5Yq1iJmKC3ihHLn3sBdFHix40DXoSapU,791
18
+ djhx_blogger-0.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ djhx_blogger-0.1.9.dist-info/entry_points.txt,sha256=3rA_ZPnqdFHrqaZ0ATmrrScDNSN-Jcs7efeyC4q2Do0,45
20
+ djhx_blogger-0.1.9.dist-info/top_level.txt,sha256=FZNu1SEldZAx_j_NmZoOxLha4G8-0KndmlFiPjPfJIk,13
21
+ djhx_blogger-0.1.9.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ blg = djhx_blogger.cli:app
@@ -1,12 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: djhx-blogger
3
- Version: 0.1.0
4
- Summary: Add your description here
5
- Requires-Python: >=3.13
6
- License-File: LICENSE
7
- Requires-Dist: beautifulsoup4>=4.14.2
8
- Requires-Dist: fabric>=3.2.2
9
- Requires-Dist: jinja2>=3.1.6
10
- Requires-Dist: markdown>=3.9
11
- Requires-Dist: typer>=0.20.0
12
- Dynamic: license-file
@@ -1,11 +0,0 @@
1
- djhx_blogger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- djhx_blogger/__main__.py,sha256=MwMXMO3kuvG-luTG4lnLwjEnqnm20lI-amV9sETColo,61
3
- djhx_blogger/cli.py,sha256=v5VmlVI5sZEFca1C5VEmi7u9GMpvNBVd3NBLZSfKpks,1445
4
- djhx_blogger/deploy.py,sha256=tHJrOHaW8vQqN-YfpRLpjZOaUDXrWk_NVXnvPM014Hc,2056
5
- djhx_blogger/gen.py,sha256=AhShduRqJWYqdBPYVXnsSQ0v4Z9QWhtRI78olNIkHl4,12409
6
- djhx_blogger/log_config.py,sha256=aEvShHNahBO52w5I6WU4U2yTMAO7qIezcH2K1vSnbVo,871
7
- djhx_blogger-0.1.0.dist-info/licenses/LICENSE,sha256=Whbb1w0-YAwWeAth-B6_jXSPWx9Fum73B0R-Z_lzUjA,1085
8
- djhx_blogger-0.1.0.dist-info/METADATA,sha256=j0rHWln9Zz5bKDABfig0ceqROGGAxjO1Y3nqgFVIpHk,325
9
- djhx_blogger-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- djhx_blogger-0.1.0.dist-info/top_level.txt,sha256=FZNu1SEldZAx_j_NmZoOxLha4G8-0KndmlFiPjPfJIk,13
11
- djhx_blogger-0.1.0.dist-info/RECORD,,