djhx-blogger 0.1.6__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/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 = {}
@@ -254,7 +260,12 @@ def gen_blog_dir(root: Node):
254
260
  with open(node.destination_path, mode='w', encoding='utf-8') as f:
255
261
  f.write(gen_article_index(node.source_path, node.source_path.parent.name))
256
262
  else:
257
- 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}')
258
269
 
259
270
  end = int(time.time() * 1000)
260
271
  logger.info(f'生成目标目录耗时: {end - start} ms')
@@ -352,6 +363,118 @@ def parse_metadata(metadata):
352
363
  return meta_dict
353
364
 
354
365
 
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
+
355
478
  def generate_blog(blog_dir: str, blog_target: str):
356
479
  start = time.time()
357
480
 
@@ -362,13 +485,17 @@ def generate_blog(blog_dir: str, blog_target: str):
362
485
  cp_resource(blog_target)
363
486
 
364
487
  end = time.time()
365
- 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
+
366
491
  return root_node
367
492
 
368
493
 
369
494
  def init_new_blog(blog_dir: str):
370
495
  blog_dir_path = Path(blog_dir) / "simple-blog" / "demo-article"
496
+ blog_images_dir_path = blog_dir_path / "images"
371
497
  blog_dir_path.mkdir(parents=True, exist_ok=True)
498
+ blog_images_dir_path.mkdir(parents=True, exist_ok=True)
372
499
  with open(blog_dir_path / 'index.md', 'w', encoding='utf-8') as file:
373
500
  file.write(f"""---
374
501
  title: "Demo Post"
@@ -380,9 +507,17 @@ summary: "simple demo article"
380
507
 
381
508
  ## title 1
382
509
 
510
+ mountain images:
511
+
512
+ ![mountain](./images/mountain.jpg)
513
+
383
514
  ### title 2
384
515
 
385
516
  This is a simple demo...
386
517
  """
387
518
  )
388
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')
Binary file
@@ -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 的模板化
@@ -2,19 +2,20 @@ djhx_blogger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  djhx_blogger/__main__.py,sha256=MwMXMO3kuvG-luTG4lnLwjEnqnm20lI-amV9sETColo,61
3
3
  djhx_blogger/cli.py,sha256=IT_hPolwqGsPbOLWwbQymfsHZtUyatgskXYue70UoFE,2013
4
4
  djhx_blogger/deploy.py,sha256=tHJrOHaW8vQqN-YfpRLpjZOaUDXrWk_NVXnvPM014Hc,2056
5
- djhx_blogger/gen.py,sha256=7XM_PQ2FXotFU4SRIQy7RAm-Pqc-iYHg8bu-AMMcVp0,13008
5
+ djhx_blogger/gen.py,sha256=nfDws6xSHAnMP2SBGq6VkT-hWkD3wIclINJ-mx8FcJw,17291
6
6
  djhx_blogger/log_config.py,sha256=aEvShHNahBO52w5I6WU4U2yTMAO7qIezcH2K1vSnbVo,871
7
7
  djhx_blogger/static/css/archive.css,sha256=Jkfl7HlQ-zsRDT4nFaR0WR7m3-AwCXsWVlLWBLdWVNM,855
8
8
  djhx_blogger/static/css/article.css,sha256=lCuMUOia2E7_E_OG7c2Gc7JMr-RdoyASQQ2mB6J2qKo,3239
9
9
  djhx_blogger/static/css/basic.css,sha256=gSKoyM_3GwoNGg3wScrVNl9ccnZwkw_ffnxODu_wbko,1152
10
10
  djhx_blogger/static/css/category.css,sha256=DBjUWZiaZO8uzpl2iY3acEVYzigW2HM3dHRfh2zNjok,2351
11
11
  djhx_blogger/static/images/favicon.ico,sha256=N0NH9yMTUzEZhGomQKeYohKP1MmL5CfmtsZi3jdlbvU,45497
12
+ djhx_blogger/static/images/mountain.jpg,sha256=lY64nDTxXm86MEKMhSZ0FaZSmKQ72tlIb-8cRl6AeC8,766801
12
13
  djhx_blogger/static/template/archive.html,sha256=aTeyirij9reMl1WmDTmZVHF0yip3c8RoAam4tj6RIo8,1340
13
14
  djhx_blogger/static/template/article.html,sha256=GgU1mZrfAd-T3Y4Z0bqOljdkGI89p-OsuCAU4IHrTwE,1246
14
15
  djhx_blogger/static/template/category.html,sha256=KRjTJw-8G3bs_LrhfeEm7xYtBON5v0pLKTPwf_PD_Uo,1607
15
- djhx_blogger-0.1.6.dist-info/licenses/LICENSE,sha256=Whbb1w0-YAwWeAth-B6_jXSPWx9Fum73B0R-Z_lzUjA,1085
16
- djhx_blogger-0.1.6.dist-info/METADATA,sha256=h4R6Mmw8PdDWE5CZMhKkrNPpzuKn7uncp012Y0yh8WY,358
17
- djhx_blogger-0.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
- djhx_blogger-0.1.6.dist-info/entry_points.txt,sha256=3rA_ZPnqdFHrqaZ0ATmrrScDNSN-Jcs7efeyC4q2Do0,45
19
- djhx_blogger-0.1.6.dist-info/top_level.txt,sha256=FZNu1SEldZAx_j_NmZoOxLha4G8-0KndmlFiPjPfJIk,13
20
- djhx_blogger-0.1.6.dist-info/RECORD,,
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,,
@@ -1,12 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: djhx-blogger
3
- Version: 0.1.6
4
- Summary: A simple static site generator, support only markdown file.
5
- Requires-Python: >=3.9
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